[HNOI2011]数学作业

洛咕

题意:给定正整数(N)(MOD),计算(Concatenate(1.. N)mod MOD) 的值,其中 $ Concatenate (1 .. N) $ 是将所有正整数(1,2,…,N)顺序连接起来得到的数.例如,(N = 13) , (Concatenate (1 .. N)=12345678910111213) .

分析:令(s_i)为前i个数得到的数,(len_i)表示i的位数,则不难写出递推式(s_i=s_{i-1}*10^{len_i}+i).发现如果(len_i)是固定的话,就是矩阵加速递推的基本做法了.再想一想,(len_i<=18),即最多只有18种可能,于是我们可以针对每一种可能都做一遍矩阵加速递推,只要考虑如何合并就好了.

先考虑转化为矩阵,假设我们已知(S(i-1)=[f(i-1),i,1]),要得到(S(i)=[f(i),i+1,1]),则可以得到转移矩阵(T=[egin{matrix} 10^{len_i} & 0 & 0 \1 & 1 & 0\0 & 1 & 1 end{matrix}]).

然后本题主要的难点在于如何合并?我们每次处理的是19,1099,100~999,我下面的代码中设置了一个for循环,让s从10开始每次乘10,还有一个now变量记录当前(上次)处理的是哪一段.所以我们当前要处理的段就是(s-1)-now.

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline LL read(){
    LL s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
LL n,mod,now;
struct matrix{
    LL a[3][3];
    matrix operator *(matrix b){//重载运算符为矩阵乘法
		matrix c;
		for(int i=0;i<3;i++)
	    	for(int j=0;j<3;j++)
				c.a[i][j]=0;
		for(int i=0;i<3;i++)
	    	for(int j=0;j<3;j++)
				for(int k=0;k<3;k++)
		    		c.a[i][j]=(c.a[i][j]+1LL*a[i][k]*b.a[k][j])%mod;
		return c;
    }
}S,T;
int main(){
    n=read();mod=read();
    S.a[0][1]=S.a[0][2]=1;//S(0)=[0,1,1]
    for(LL s=10;now<n;s*=10){
		T.a[1][0]=T.a[1][1]=T.a[2][1]=T.a[2][2]=1;
		T.a[0][1]=T.a[0][2]=T.a[1][2]=T.a[2][0]=0;
		T.a[0][0]=s%mod;//10^len
		LL b=min(s-1,n)-now;
//当前处理的段,这里s-1和n取min是要考虑到实际情况
		while(b){//直接矩阵快速幂
	    	if(b&1)S=S*T;
	    	T=T*T;
	    	b>>=1;
		}
		now=min(s-1,n);//now记录当前处理的段
    }
    printf("%lld
",S.a[0][0]);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/10644914.html