某年元宵节大礼包 矩阵快速幂

题目描述

  乐是做作业,给定正整数N,M,要求计算Concatenate(1……N)mod M的值,其中Concatenate是指将1到N拼起来得到的数。如N=13,Concatenate=12345678910111213

输入格式

  一行两个整数,N,M

输出格式

  一个非负整数表示计算结果

样例

输入  13 13

输出  4

数据范围

  n<=1018,m<=109

分析

  第一眼看到这题:暴力模拟

  然后看到数据表示无语,1e18啊,这拼起来得多大

  不管了先弄一个模拟:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
unsigned long long s;
int n, m;
int wei(int x) {
    int s = 0;
    while (x) {
        s++;
        x /= 10;
    }
    return s;
}
int main() {
    freopen("le.in", "r", stdin);
    freopen("le.out", "w", stdout);
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        s = (s * pow(10, wei(i)) + i),
        s = s % m;
    cout << s;
    return 0;
}

  这个应该很好懂,就不解释了,30分

  作为一个合格的OI选手,一定是要考虑正解的。

设ans[i]表示拼接到i位时的答案,我们很容易得到递推式:

  ans[i]=(ans[i-1]*p+i)%mod; 

其中p表示当前i的位数乘十,先不考虑mod,比如ans[4]=123*10+4;

于是这又是一个递推式问题,那么怎么办呢?暴力跑一遍?1e18的数据不TLE算我输

所以这个时候矩阵快速幂来了……

可以设一个矩阵A为

  {ans[i]  i  1} 反正我是习惯开一维数组。。。。

然后另一个矩阵K为

  {p 0 0}

  {1 1 0}

  {1 1 1}

这样A*K就能得到{ans[i]*p+i  i+1  1}即{ans[i+1]  i+1  1}

是不是很巧妙,所以不难看出上述A中的1的作用——与ans[i],凑出ans[i+1]和i+1.

接着考虑乘多少次方的问题,我们从i=0开始,如果n=4,那么显然要乘四次;

如果n=14呢?从0到9,一共要乘9次,然后再从10到14乘5次

接着多次进行迭代不难发现,我们可以用变量data拷贝n

如果n>=p就乘9*p/10次,data减去乘的值

当n<p时说明不够乘了,所以直接乘以data次

最后跑快速幂就行了

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
ull n,mod,a[4],c[4][4];
void Mul(){
    ull cp[4];
    memset(cp,0,sizeof(cp));
    for(int i=1;i<=3;i++){
        for(int j=1;j<=3;j++){
            cp[i]+=((a[j]%mod)*(c[j][i]%mod))%mod;
            //乘之前一定要mod一下不然两个数乘积可能超出longlong
            cp[i]%=mod;
        }  
    }
    memcpy(a,cp,sizeof(cp));
}
void Mulself(){
    ull cp[4][4];
    memset(cp,0,sizeof(cp));
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            for(int k=1;k<=3;k++){
                cp[i][j]+=((c[i][k]%mod)*(c[k][j]%mod))%mod;
                cp[i][j]%=mod;
            }
    memcpy(c,cp,sizeof(cp));
}
int main(){
    scanf("%llu%llu",&n,&mod);
    a[1]=0;a[2]=0;a[3]=1;
    ull mx=n*10,data=n;
    for(ull p=10LL;p<=mx;p*=10LL){
        c[1][1]=p;c[1][2]=0;c[1][3]=0;
        c[2][1]=1;c[2][2]=1;c[2][3]=0;
        c[3][1]=1;c[3][2]=1;c[3][3]=1;
        ull x;
        if(n>=p){
            x=9LL*p/10LL;
            data-=x;
        }
        else x=data;
        while(x){
            if(x&1)Mul();
            x>>=1;
            Mulself();
        }
    }
    printf("%llu\n",a[1]);
}

 

 

 

posted @ 2020-03-07 11:45  An_Fly  阅读(269)  评论(0编辑  收藏  举报