题解 P3216 【[HNOI2011]数学作业】

· · 题解

/* * P:3216

* Au:Small_Ash

* 嗯,一开始看题的时候就觉得肯定是O(N)的奇怪算法(毕竟是一直连接一个数啊,肯定要连接一次取模一次)

* 然后数据范围给了我一巴掌!10^18 Are you sure? 嗯,那就是O(logN)没跑了

* 等等啊,我们看看第N个数的计算方法啊,就是前一个数接数嘛。。

* 等等,接数,等等 ,前一个? N-1=>N 。。。

* 我好想发现了什么,递推?? f[N]=f[N-1]*10^k+N;(k是N的位数)

* 等等,f[N]哪门子开的出来啊。。。

* 等等,递推,logN。。。

* 。。。

* 哦,矩阵快速幂!

* 然后就有了思路。。。

* {10^k,0,0}

* {F[n],n,1}= { 1, 1,0} * {F[n-1],n-1,1} (f[N]=f[N-1]*10^k+N可变形为f[N]=f[N-1]*10^k+(N-1)+1)

* { 1, 1,1}

* * OK!然后就是实现了。。。

* 三点提醒

* 1.全部用long long,不要吝啬内存。

* 2.凡是有 long long*long long 的计算全部改成 ((long long)%p*(long long)%p)%p

* (别问我为什么会知道,我会说我之前所有带取模的递推都是死在取模不够彻底上吗。。。)

* 3.10^k的计算要分段,这段的实现可能会让你抓狂,忍住慢慢改,这里给一组大数据供各位调试。

* Input : 948177742992423376 819143553 Output : 578278753

* 以上,下面是代码。

*/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
long long n,p;
struct zj{
    long long a[5][5];
    zj operator = (int k){
        a[1][1]=1;
        for (int i=1;i<=k;i++)
        a[1][1]*=10;
        a[1][1]%=p;
        a[1][2]=0;
        a[1][3]=0;
        a[2][1]=1;
        a[2][2]=1;
        a[2][3]=0;
        a[3][1]=1;
        a[3][2]=1;
        a[3][3]=1;
        return *this;
    }
    zj operator * (zj b){
        zj c;
           for (int i=1;i<=3;i++)
             for (int j=1;j<=3;j++){
                 c.a[i][j]=0;
                 for (int k=1;k<=3;k++){
                    c.a[i][j]+=(a[i][k])%p*(b.a[k][j])%p;  
                    c.a[i][j]%=p;
                }
            }
        return c;
    }
    zj operator ^ (long long x){
        zj c,a;a=*this;
           for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                c.a[i][j]=(i==j); 
        for(;x;x>>=1){
            if(x&1) c=c*a;
               a=a*a;
        }
        return c;
    }
}A[20],B;
long long pow10(int x){
    long long out=1;
    for (int i=1;i<=x;i++) out*=10;
    return out;
}
int main(){
    scanf("%lld%lld",&n,&p);
    int k=0;long long nn=n;
    while (nn) {k++;nn/=10;}
    for (int i=1;i<k;i++){
        A[i]=i;
        A[i]=A[i]^(pow10(i-1)*9);
    }
    nn=n-pow10(k-1)+1;
    A[k]=k;
    A[k]=A[k]^nn;
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            B.a[i][j]=(i==j);
    for (int i=1;i<=k;i++) B=B*A[i];
    printf("%lld",B.a[3][1]);
    return 0;
}