BZOJ2326: [HNOI2011]数学作业
Description
小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:
给定正整数 N 和 M要求计算 Concatenate (1 .. N) Mod M 的值,其中 Concatenate (1 ..N)是将所有正整数 1, 2, …, N 顺序连接起来得到的数。
例如,N = 13, Concatenate (1 .. N)=12345678910111213.
小C 想了大半天终于意识到这是一道不可能手算出来的题目,于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。
Input
只有一行且为用空格隔开的两个正整数N和M,1≤N≤10^18且1≤M≤10^9.
Output
仅包含一个非负整数,表示 Concatenate (1 .. N) Mod M 的值。
Sample Input
13 13
Sample Output
4
题解Here!
看到$N<=10^{18}$,不是矩乘就是跟$N$无关。
当然这题不可能与$N$无关,那就矩乘。
首先,要推出一个线性递推的式子。
然而一开始我把题目看错了,推出了这样一个式子:$$f(i)=f(i-1)\times 10+i$$
觉得这不是沙茶题吗?
这是一个$flag$。。。
放到矩阵里就是:$$f(i)=f(i-1)\times 10+i-1+1$$
造出矩阵:$$\left[\begin{array}{}10&0&0\\1&1&0\\1&1&1 \end{array}\right]$$
我沾沾自喜的交了上去,以为能$1A$。
然后$WA\quad 0$。
$flag$不能乱立啊。。。
回头看题,我发现我就是个$zz$。。。
递推式应该是:$$f(i)=f(i-1)\times 10^k+i,\text{k为i的位数}$$
继续造矩阵:$$\left[\begin{array}{}10^k&0&0\\1&1&0\\1&1&1 \end{array}\right]$$
$10^k$的计算要分段,这段的实现可能会让你抓狂,忍住!慢慢改。
这样我们可以求出:$$Concatenate(1...9),Concatenate(10...99),Concatenate(100...999),...,Concatenate(10^k,N)$$
因为$k$的取值只有不到$20$种,我们可以直接枚举处理。
供应一组数据:
Input 948177742992423376 819143553 Output 578278753
附代码:
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; int k=0; long long n,mod; struct node{ long long a[5][5]; friend node operator +(node x,int k){ x.a[1][1]=1; for(int i=1;i<=k;i++)x.a[1][1]*=10LL; x.a[1][1]%=mod; x.a[1][2]=x.a[1][3]=x.a[2][3]=0; x.a[2][1]=x.a[2][2]=x.a[3][1]=x.a[3][2]=x.a[3][3]=1; return x; } friend node operator *(const node x,const node y){ node ret; for(int i=1;i<=3;i++) for(int j=1;j<=3;j++){ ret.a[i][j]=0; for(int k=1;k<=3;k++){ ret.a[i][j]+=x.a[i][k]%mod*y.a[k][j]%mod; ret.a[i][j]%=mod; } } return ret; } friend node operator ^(node x,long long k){ node s; for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) s.a[i][j]=(i==j?1:0); while(k){ if(k&1)s=s*x; x=x*x; k>>=1; } return s; } }base[20],ans; inline long long read(){ long long date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } long long mexp(long long a,long long b){ long long s=1; while(b){ if(b&1)s=s*a; a*=a; b>>=1; } return s; } void work(){ for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) ans.a[i][j]=(i==j?1:0); for(int i=1;i<=k;i++)ans=ans*base[i]; printf("%lld\n",ans.a[3][1]); } void init(){ long long x; x=n=read();mod=read(); while(x){x/=10LL;k++;} for(int i=1;i<k;i++){ base[i]=base[i]+i; base[i]=base[i]^(mexp(10,i-1)*9LL); } base[k]=base[k]+k; base[k]=base[k]^(n-mexp(10,k-1)+1); } int main(){ init(); work(); return 0; }