题解 P3216 【[HNOI2011]数学作业】

P3216 [HNOI2011]数学作业

这一道题

……

\[n\leq 10^{18} \]

这一看就要用矩阵乘法

我们先写出递推式

\[f(n+1)=f(n)\times 10^{calc(n+1)}+(n+1) \]

这其中,calc代表的是n+1在十进制中有多少位

既然有calc,那么我们自然而然得想到划分

因为数位是连续的,所以将(19),(1099),(100~999)……(1e17+1,1e18-1)划分成区间

这时,calc就可以当成定值了

然后我们就可以写出矩形递推式了

\[\begin{bmatrix}f_{n+1}\\n+2\\1\end{bmatrix} = \begin{bmatrix} calc&\;1&\;0 \\ 0 &\;1&\;1 \\ 0&\;0&\;1 \end{bmatrix} \times \begin{bmatrix}f_{n}\\n+1\\1\end{bmatrix} \]

然后时间复杂度就是

\[O(log^3(n)) \]

解决


#include<cstdio>
#define Starseven main
#define ll long long
ll mod, S[20];

struct matrix {
	ll va[4][4];
	int line, cross;
	void Mem() {
		for (int i = 1; i <= 3; i++) {
			for (int j = 1; j <= 3; j++) {
				va[i][j] = 0;
			}
		}
		return ;
	}
};

matrix operator *(const matrix &a, const matrix &b) {
	matrix c;
	c.Mem();
	for (int i = 1; i <= a.line; i++) {
		for (int k = 1; k <= a.cross; k++) {
			for (int j = 1; j <= b.cross; j++) {
				c.va[i][j] = (c.va[i][j] + a.va[i][k] * b.va[k][j] % mod) % mod;
			}
		}
	}
	c.line = a.line;
	c.cross = b.cross;
	return c;
}

int Starseven(void) {
	ll n;
	read(n);
	read(mod);
	matrix ans, txt;
	
	ans.Mem();
	ans.va[1][1] = 1;
	ans.va[2][1] = 2;
	ans.va[3][1] = 1;
	ans.line = 3;
	ans.cross = 1;
	
	ll hack = 1;
	for (int i = 1; i <= 18; i++) {
		S[i] = hack * 10;
		S[i] -= hack;
		hack *= 10; 
	}
	hack = 10; 
	for (ll i = 1; i <= 18; i++) {
		txt.Mem();
		
		txt.va[1][1] = hack % mod;
		txt.va[1][2] = 1;
		txt.va[2][2] = 1;
		txt.va[2][3] = 1;
		txt.va[3][3] = 1;
		txt.line = 3;
		txt.cross = 3;
		
		if(n >= S[i]) {
			ll b = S[i];
			if(i == 1) b -= 1;
			while(b) {
				if(b & 1ll) ans = txt * ans;
				txt = txt * txt;
				b >>= 1ll;
			}
			n -= S[i];
		}
		else {
			if(i == 1) n -= 1;
			while(n) {
				if(n & 1ll) ans = txt * ans;
				txt = txt * txt;
				n >>= 1ll;
			}
			break;
		}
		hack = (hack * 10) % mod;
	}
	write(ans.va[1][1]);
	puts("");
	return 0;
} 
posted @ 2020-08-25 20:59  starseven  阅读(154)  评论(0编辑  收藏  举报