Solution -「洛谷 P2044」「NOI 2012」随机数生成器

cirnovsky /

§ Description

Link.

给你一个递推式,让你求某一项的值模上 gg

§ Solution

这道题正解是矩阵。我这里给出一种分治的做法。

题目中说

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ Xi=(a×Xi1+c) mod mX_{i}=(a\times X_{i-1}+c)\ \mathrm{mod}\ m

我们先往下推一步

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ Xi1=(a×Xi2+c) mod mX_{i-1}=(a\times X_{i-2}+c)\ \mathrm{mod}\ m

我们把这个式子代入到原式,得到

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ XiX_{i}

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ =(a×Xi1+c) mod m=(a\times X_{i-1}+c)\ \mathrm{mod}\ m

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ =(a×(a×Xi2+c)+c) mod m=(a\times(a\times X_{i-2}+c)+c)\ \mathrm{mod}\ m

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ =a2×Xi2+c×(a+1) mod m=a^{2}\times X_{i-2}+c\times(a+1)\ \mathrm{mod}\ m

按照这个套路推下去,最后得到:

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ Xi=ai×X0+c×(ai1+ai2++a+1)X_{i}=a^{i}\times X_{0}+c\times(a^{i-1}+a^{i-2}+\cdots+a+1)

ai×X0a^{i}\times X_{0} 很好得到,直接大力快速幂,再乘上 X0X_{0} 即可。

我们接着来看后面的

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ c×(ai1+ai2++a+1)c\times(a^{i-1}+a^{i-2}+\cdots+a+1)

先不要看 cc,即。

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ ai1+ai2++a+1a^{i-1}+a^{i-2}+\cdots+a+1

相信大家都学过因式分解,对于这样的式子进行因式分解简直再容易不过了。如果最高次为奇数次,那么我们可以直接两两分组,就可以提出来,即:

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ ai1+ai2++a+1a^{i-1}+a^{i-2}+\cdots+a+1

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ =(ai1+ai2)+(ai3+ai4)++(a+1)=(a^{i-1}+a^{i-2})+(a^{i-3}+a^{i-4})+\cdots+(a+1)

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ =ai2×(a+1)+ai4×(a+1)++(a+1)=a^{i-2}\times(a+1)+a^{i-4}\times(a+1)+\cdots+(a+1)

       \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \        \ \ \ \ \ \ \ =(a+1)×(ai2+ai4++a2+1)=(a+1)\times(a^{i-2}+a^{i-4}+\cdots+a^{2}+1)

这样我们就可以一直递归分治下去解决问题了,最后再乘上一个 CC 即可。

至于最高次为偶次就直接单独提出来大力快速幂即可。

还有一个细节,这道题的乘法常数过大,需要用“快速乘”。其实跟快速幂差不多。

#include <cstdio>

char buf[1 << 21], *p1 = buf, *p2 = buf;
#ifndef ONLINE_JUDGE
#define gc() getchar()
#else
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
#endif
#define is_number (ch >= '0' && ch <= '9')

template < typename Type >
void read(Type& a) {
	a = 0; bool f = 0; char ch;
	while (!(ch = gc(), is_number)) if (ch == '-') f = 1;
	while (is_number) a = (a << 3) + (a << 1) + (ch ^ '0'), ch = gc();
	a = (f ? -a : a);
}

template < typename Type, typename... Args >
void read(Type& t, Args&... args) {
	read(t), read(args...);
}

typedef long long LL;
LL MOD, a, c, X0, n, g;

LL fast_mul(LL x, LL y) {
	LL res = 0;
	for (; y; y >>= 1, x = (x + x) % MOD)
		if (y & 1) res = (res + x) % MOD;
	return res % MOD;
}

LL fast_pow(LL x, LL y) {
	LL res = 1;
	for (; y; y >>= 1, x = fast_mul(x, x))
		if (y & 1) res = fast_mul(res, x);
	return res % MOD;
}

LL get_sum(LL x, LL y) {
	if (y == 0) return 1;
	else if (y == 1) return (x + 1) % MOD;
	else if (y & 1) return fast_mul((fast_pow(x, (y >> 1) + 1) % MOD + 1) % MOD, get_sum(x, y >> 1) % MOD) % MOD;
	else return fast_mul((fast_pow(x, y >> 1) + 1) % MOD, get_sum(x, (y >> 1) - 1) % MOD) % MOD + fast_pow(x, y) % MOD;
}

signed main() {
	read(MOD, a, c, X0, n, g);
	X0 %= MOD;
	printf("%lld\n", (fast_mul(fast_pow(a, n) % MOD, X0) % MOD + fast_mul(get_sum(a, n - 1) % MOD, c % MOD) % MOD) % MOD % g);
	return 0;
}