1. 程式人生 > >【bzoj】2326 [HNOI2011]數學作業

【bzoj】2326 [HNOI2011]數學作業

gif 高位到低位 algo space n) n+1 const std class

【題意】給定n和m,求1~n從高位到低位連接%m的結果。n=11時,ans=1234567891011%m。n<=10^18,m<=10^9。

【算法】遞推+矩陣快速冪

【題解】

考慮枚舉位數個數k,對於不同的k單獨遞推,設f[i]表示1~i的答案,則有:

$$f_n=f_{n-1}*10^k+i$$

轉化為矩陣遞推式,則有:

$$\begin{vmatrix}10^k & 1 & 1\\ 0 & 1 & 1\\0 & 0 & 1\end{vmatrix} \times \begin{vmatrix}f_n \\ n\\1 \end{vmatrix}=\begin{vmatrix}f_{n+1}\\n+1\\1\end{vmatrix}$$

轉化為冪形式,則有:

$$\begin{vmatrix}10^k & 1 & 1\\ 0 & 1 & 1\\0 & 0 & 1\end{vmatrix}^n \times \begin{vmatrix}f_i \\ i\\1 \end{vmatrix}=\begin{vmatrix}f_{i+n}\\i+n\\1\end{vmatrix}$$

分段進行矩陣快速冪即可。

註意讀入的n是long long。

技術分享圖片
#include<cstdio>
#include<cstring>
#include<algorithm>
#define
ll long long using namespace std; const int N=3; int c[N][N],ANS[N][N],A[N][N],m; ll n;/// void multply(int a[N][N],int b[N][N]){ for(int i=0;i<=2;i++){ for(int j=0;j<=2;j++){ c[i][j]=0; for(int k=0;k<=2;k++){ c[i][j]=(c[i][j]+1ll*a[i][k]*b[k][j]%m)%m; } } }
for(int i=0;i<=2;i++)for(int j=0;j<=2;j++)b[i][j]=c[i][j]; } void solve(ll p,int &f,int x,int k){ memset(A,0,sizeof(A)); A[0][1]=A[0][2]=A[1][1]=A[1][2]=A[2][2]=1;A[0][0]=k; ANS[0][0]=f;ANS[1][0]=x%m;ANS[2][0]=1; while(p){ if(p&1)multply(A,ANS); multply(A,A); p>>=1; } f=ANS[0][0]; } int main(){ scanf("%lld%d",&n,&m); ll x=0,y=9; int k=10%m,ans=0; while(x+y<n){ solve(y,ans,x%m,k);// x+=y;y*=10;k=1ll*k*10%m; } solve(n-x,ans,x%m,k); printf("%d",ans%m); return 0; }
View Code

【bzoj】2326 [HNOI2011]數學作業