【HNOI2011】数学作业

题目描述

  小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:

  给定正整数 N 和 M ,要求计算Concatenate(1..N) Mod M 的值,其中 Concatenate(1..N) 是将所有正整数 1,2,…,N 顺序连接起来得到的数。例如,N=13 , Concatenate(1..N)=12345678910111213 .小C 想了大半天终于意识到这是一道不可能手算出来的题目,于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。

分析

  彩笔还是没有思路啊~

  其实本题用到的思路是,当你求一个数对某一个值取模的时候,可以按位取模,即从高位向低位求这个数的过程中不断取模。可是这样的话,复杂度就是O(n)了,n是1e18的,我勒个去。

  嘿嘿,递推过不去?上矩阵快速幂啊!不过这道题有点小麻烦,你列出式子,f【n】=f【n-1】*10^k+n,k是n的位数。矩阵加速递推的时候,系数必须是固定不变的,现在这样的话,我们无法进行递推。所以需要对数进行分块处理,每次处理相同位数的数,做一次矩阵快速幂。还有就是把n拆成n-1+1,这样就和f【n-1】保持一致了。设计1*3的状态矩阵与3*3的状态转移矩阵,做完快速幂之后f【0】【0】就是答案。

  这道题比较神奇的是两个数相乘之前一定取模,没取模就奇怪的Wa了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll n,mod,t,A[3][3],f[3][3];
void mul(ll a[3][3],ll b[3][3],ll c[3][3]){
    ll tmp[3][3]={};
    for(int i=0;i<3;++i)
        for(int j=0;j<3;++j)
            for(int k=0;k<3;++k)
                tmp[i][j]=(tmp[i][j]+(a[i][k]%mod)*(b[k][j]%mod))%mod;
    for(int i=0;i<3;++i)
        for(int j=0;j<3;++j)
            c[i][j]=tmp[i][j];
}
void calc(ll t,ll last){
    memset(A,0,sizeof(A));
    A[0][0]=t;A[1][1]=1;
    A[2][2]=1;A[1][0]=1;
    A[2][0]=1;A[2][1]=1;
    ll y=last-t/10+1;
    for(;y;y>>=1){
        if(y&1) mul(f,A,f);
        mul(A,A,A);
    }
}
int main(){
    scanf("%lld%lld",&n,&mod);
    if(mod==1){
        printf("0\n");
        return 0;
    }else{
        f[0][2]=1;
        t=10;
        while(n>=t){
            calc(t,t-1);
            t*=10;
        }
        calc(t,n);
    }
    printf("%lld\n",f[0][0]);
    return 0;
}
speech.gif posted on 2019-01-18 14:39 kgxpbqbyt 阅读( ...) 评论( ...) 编辑 收藏

猜你喜欢

转载自blog.csdn.net/qq_39759315/article/details/88403726