【bzoj】2326 [HNOI2011]数学作业

【题意】给定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

 

posted @ 2018-04-10 11:33  ONION_CYC  阅读(...)  评论(...编辑  收藏