bzoj2326&CodeVS2314 数学作业

http://www.lydsy.com/JudgeOnline/problem.php?id=2326 http://codevs.cn/problem/2314/

题意:求123456789101112……n对m取模的结果。n<=10^18,m<=10^9。

记f(i)=Concatenate(1...i),则有f(i+1)=f(i)*10^k+i+1,其中k为i+1的位数。

首先,对于连续的一段递推式,10^k的值是不变的,因此可以考虑分段进行矩阵乘法快速幂来优化递推。

但是这里下标i的值也参与了计算,因此我们可以考虑维护一个值使其总是等于下标,于是我们有:

[ f(i), i, 1 ] * [ base, 0, 0 ] = [ f(i+1), i+1, 1 ]
   [ 1,        1, 0 ]
   [ 1,        1, 1 ]

[ f(i), i+1, 1 ] * [ base, 0, 0 ] = [ f(i+1), i+2, 1 ]
        [ 1,        1, 0 ]
        [ 0,        1, 1 ]

我写的是后者。

代码:

#include<cstdio>
#include<cstring>
#define rpt(i,l,r) for(i=l;i<=r;i++)
long long n,k;
int m;
struct matrix{
    int v[3][3];
    void print(){
        int i,j;
        rpt(i,0,2){
            rpt(j,0,2) printf("%d ",v[i][j]);
            printf("\n");
        }
    }
}a,b,c;
matrix operator *(matrix a,matrix b){
    matrix res;
    int i,j,k;
    memset(res.v,0,sizeof(res.v));
    rpt(i,0,2) rpt(j,0,2) rpt(k,0,2)
        (res.v[i][j]+= (int)((long long)a.v[i][k]*b.v[k][j]%m) )%=m;
    return res;
}
const matrix I=(matrix){1,0,0,0,1,0,0,0,1};
matrix operator ^(matrix a,long long x){
    matrix res=I;
    while(x){
        if(x&1) res=res*a;
        x>>=1;a=a*a;
    }
    return res;
}
int main(){
    scanf("%lld%d",&n,&m);
    a=(matrix){0,1,1,0,0,0,0,0,0};
    b=(matrix){10,0,0,1,1,0,0,1,1};
    k=1;
    while(k<=n/10){
        a=a*(b^(k*10-k));
        k*=10;
        b.v[0][0]=(int)(k*10%m);
    }
    a=a*(b^(n-k+1));
    printf("%d",a.v[0][0]);
}

你可能感兴趣的:(bzoj2326&CodeVS2314 数学作业)