bzoj2326 [HNOI2011]数学作业 矩阵乘法

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/83012557

Description


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

Solution


有一个十分显然的柿子是f[i]=f[i-1]*10^k+i,其中f[i]表示i的答案,k由i的位数决定
注意到当i的位数相同时k也是相同的,因此我们按照1~ 9,10~ 99,100~ 999这样的顺序做
f[i]的转移只和f[i-1]与i与1有关,矩阵加速即可

注意longlong变量模一个int变量的时候要考虑本来就比模数大的情况,没注意这个挂了40’QUQ

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef unsigned long long LL;

int MOD;

struct Mat {
	LL r[4][4];

	Mat() {fill(r,0);}

	Mat operator *(Mat B) const {
		Mat A=*this,C;
		rep(i,1,3) rep(j,1,3) {
			rep(k,1,3) C.r[i][j]=(C.r[i][j]%MOD+A.r[i][k]*B.r[k][j]%MOD)%MOD;
		}
		return C;
	}

	Mat operator ^(LL dep) const {
		Mat A=*this,C=*this; dep--;
		for (;dep;dep>>=1) {
			if (dep&1) C=C*A;
			A=A*A;
		}
		return C;
	}
} A,B;

int main(void) {
	LL n; scanf("%llu%d",&n,&MOD);
	LL ans=0,i=1;
	while (i<=(long double)n/10.0) {
		fill(A.r,0); A.r[1][1]=i*10LL%MOD;
		A.r[2][1]=A.r[2][2]=A.r[3][1]=A.r[3][2]=A.r[3][3]=1;
		A=A^(i*9);
		ans=((ans%MOD)*(A.r[1][1]%MOD)%MOD+(A.r[2][1]%MOD)*((i-1)%MOD)%MOD+A.r[3][1])%MOD;
		i=i*10LL;
	}
	fill(A.r,0); A.r[1][1]=i*10LL%MOD;
	A.r[2][1]=A.r[2][2]=A.r[3][1]=A.r[3][2]=A.r[3][3]=1;
	A=A^(n-i+1);
	ans=((ans%MOD)*(A.r[1][1]%MOD)%MOD+(A.r[2][1]%MOD)*((i-1)%MOD)%MOD+A.r[3][1])%MOD;
	printf("%llu\n", ans);
	return 0;
/*	ans=0;
	rep(i,1,std:: min(9LL,n)) ans=(ans*10LL%MOD+i)%MOD;
	rep(i,10,std:: min(99LL,n)) ans=(ans*100LL%MOD+i)%MOD;
	rep(i,100,std:: min(999LL,n)) ans=(ans*1000LL%MOD+i)%MOD;
	rep(i,1000,std:: min(9999LL,n)) ans=(ans*10000LL%MOD+i)%MOD;
	rep(i,10000,std:: min(99999LL,n)) ans=(ans*100000LL%MOD+i)%MOD;
	rep(i,100000,std:: min(999999LL,n)) ans=(ans*1000000LL%MOD+i)%MOD;
	rep(i,1000000,std:: min(9999999LL,n)) ans=(ans*10000000LL%MOD+i)%MOD;
	printf("%lld\n", ans);
*/}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/83012557