[FOI2020WC模拟]看上去很简单

280 篇文章 1 订阅

题目

题目描述
道生一,一生二,二生三,三生万物。由此可知, g i = 3 g i − 1 g_i=3g_{i-1} gi=3gi1

天之道,损有余而补不足。故而真正的 g i = 3 g i − 1 − g i − 2 g_i=3g_{i-1}-g_{i-2} gi=3gi1gi2

世事如棋局局新。 g 0 = α ,    g 1 = β g_0=\alpha,\;g_1=\beta g0=α,g1=β,此皆变者也。

子子孙孙无穷尽也,而值加增,何苦不知 f n , k = f g n , k − 1 f_{n,k}=f_{g_n,k-1} fn,k=fgn,k1 之值也!

天下有道,丘不与易也。 f n , 0 = n f_{n,0}=n fn,0=n,此不变者也。

数据范围与提示
数据组数 T ≤ 1 0 3 T\le 10^3 T103,而 n ≤ 1 0 9 n\le 10^9 n109,好在 k ≤ 1 0 2 k\le 10^2 k102

为了方便输出,只需要求出模 p p p 意义下的值。 p ≤ 1 0 9 p\le 10^9 p109,每组数据都不一样。

思路

好像 g n g_n gn 不能直接取模 p p p,因为要求 g g n g_{g_n} ggn 的值……
G ( x ) − a − b x = ( 3 x − x 2 ) ⋅ G ( x ) − 3 a x ( x 2 − 3 x + 1 ) ⋅ G ( x ) = ( b − 3 a ) x + a G(x)-a-bx=(3x-x^2)\cdot G(x)-3ax\\\quad\\ (x^2-3x+1)\cdot G(x)=(b-3a)x+a G(x)abx=(3xx2)G(x)3ax(x23x+1)G(x)=(b3a)x+a

我们有理由相信 g n g_n gn 会循环,因为只有 p 2 p^2 p2 种相邻元素的取值。而一旦这个重复,后面就都一样了。可是怎么找啊?

考虑 a , b a,b a,b 的系数分别计算。对于 b b b 的系数,相当于初值 a = 0 ,    b = 1 a=0,\;b=1 a=0,b=1,即
( x 2 − 3 x + 1 ) ⋅ G b ( x ) = x b g n = 1 5 [ ( 3 + 5 2 ) n − ( 3 − 5 2 ) n ] (x^2-3x+1)\cdot G_b(x)=x\\\quad\\ bg_n=\frac{1}{\sqrt{5}} \left[ \left(\frac{3+\sqrt 5}{2}\right)^n -\left(\frac{3-\sqrt 5}{2}\right)^n \right] (x23x+1)Gb(x)=xbgn=5 1[(23+5 )n(235 )n]

n = p n=p n=p 时, ( n i ) {n\choose i} (in) 只有 i ∈ { 0 , p } i\in\{0,p\} i{0,p} 可以模 p p p 不为零,于是只剩 ( 5 ) p 2 p − 1 (\sqrt{5})^p\over 2^{p-1} 2p1(5 )p 。然后外面有个 1 5 1\over\sqrt{5} 5 1,总体就是 5 p − 1 2 2 p − 1 ≡ 5 p − 1 2 ∈ { − 1 , 1 } {5^{p-1\over 2}\over 2^{p-1}}\equiv 5^{p-1\over 2}\in\{-1,1\} 2p152p152p1{1,1} 。(特殊情况也找到了,就是 p = 5 p=5 p=5 嘛。)

n = p + 1 n=p+1 n=p+1 呢?也不难计算出,其值为 3 2 ( 1 + 5 p − 1 2 ) \frac{3}{2}(1+5^{p-1\over 2}) 23(1+52p1) 。那么我们可以分类讨论一下:

  • 5 p − 1 2 ≡ − 1 5^{p-1\over 2}\equiv -1 52p11,那么 b g n + 1 = 0 ,    b g n + 2 = 1 bg_{n+1}=0,\;bg_{n+2}=1 bgn+1=0,bgn+2=1,于是它的周期是 n + 1 n+1 n+1
  • 5 p − 1 2 ≡ 1 5^{p-1\over 2}\equiv 1 52p11,那么 b g n + 1 = 3 bg_{n+1}=3 bgn+1=3,解方程 3 b g n − b g n − 1 = b g n + 1 3bg_n-bg_{n-1}=bg_{n+1} 3bgnbgn1=bgn+1 可知 b g n − 1 = 0 bg_{n-1}=0 bgn1=0,于是存在一个 n − 1 n-1 n1 的周期。

现在 b b b 的周期得证了。我们再来看 a a a 。它相当于初值 a = 1 ,    b = 0 a=1,\;b=0 a=1,b=0,即
( x 2 − 3 x + 1 ) ⋅ G a ( x ) = − 3 x + 1 a g n = 3 5 − 5 10 ( 3 + 5 2 ) n + 15 − 3 5 10 ( 3 − 5 2 ) n (x^2-3x+1)\cdot G_a(x)=-3x+1\\\quad\\ ag_n=\frac{3\sqrt 5-5}{10} \left({3+\sqrt{5}\over 2}\right)^n +{15-3\sqrt 5\over 10}\left(\frac{3-\sqrt 5}{2}\right)^n (x23x+1)Ga(x)=3x+1agn=1035 5(23+5 )n+101535 (235 )n

类似的,最后都归结到 5 p − 1 2 5^{p-1\over 2} 52p1 上面去。总之一句话:最小循环节要么是 p − 1 p-1 p1 的因数,要么是 p + 1 p+1 p+1 的因数虽然考试的时候我是直接打表发现的规律

当然,如果你更细心一点,你会发现,若 p   m o d   5 ∈ { 1 , 4 } p\bmod 5\in\{1,4\} pmod5{1,4} 那么 5 p − 1 2 ≡ 1 5^{p-1\over 2}\equiv 1 52p11,否则 5 p − 1 2 ≡ − 1 5^{p-1\over 2}\equiv -1 52p11 。因为 5 p − 1 2 = ( 5 p ) = ( p 5 ) 5^{p-1\over 2}=\left(5\over p\right)=\left(p\over 5\right) 52p1=(p5)=(5p) 嘛(这是勒让德符号,用了二次互反律),而模 5 5 5 的二次剩余仅有 1 , 4 1,4 1,4 两个。不知道这个也没关系,暴力检查一下    p + 1    \sout{\;p+1\;} p+1是不是周期也可以判断

那么 p 2 p^2 p2 呢?打表发现,它是 p ⋅ T p p\cdot T_p pTp 或者 T p T_p Tp 。具体的证明差不多还是用卢卡斯定理。推广开来, T p k ∣ p k ⋅ T p T_{p^k}\mid p^{k}\cdot T_p TpkpkTp我们有理由相信,它是读者自证不难的。

现在,我们知道 g ( n )   m o d   p g(n)\bmod p g(n)modp 循环节是 T p T_p Tp,那么我们求 g [ g ( n ) ]   m o d   p g[g(n)]\bmod p g[g(n)]modp 只需要求 g ( n )   m o d   T p g(n)\bmod T_p g(n)modTp 即可,这是一个递归的过程。

每次递归都要 O ( p ) \mathcal O(\sqrt p) O(p ) 做质因数分解吗?其实也可以存下来。但是并没有快多少。

而且,根据上面的过程可知,最大的质因数如果次数为 1 1 1,那么每次都至少变小一半。很快 p p p 就会塌缩成只有 2 , 3 , 5 2,3,5 2,3,5 构成。最后会塌缩到 2 2 × 3 × 5 t    ( t ∈ N + ) 2^2\times 3\times 5^{t}\;(t\in\N^+) 22×3×5t(tN+) 2 2 × 3 2^2\times 3 22×3

哪怕是 2 × 3 × 5 × ⋯ × 41 = 304 , 250 , 263 , 527 , 210 2\times 3\times 5\times\cdots\times 41=304,250,263,527,210 2×3×5××41=304,250,263,527,210 这样的庞然大物,它也只能变成 10 , 835 , 137 , 422 , 950 , 400 10,835,137,422,950,400 10,835,137,422,950,400,大概是原来的 35 35 35 倍而已……但是 l c m \rm lcm lcm 的过程中,丢失的质因数是非常多的。一方面,最大的质因数只能提供一个 p + 1 p \frac{p+1}{p} pp+1,然后就会立刻被分解(因为 2 ∣ ( p + 1 ) 2\mid(p+1) 2(p+1),至少要缩小到原来的一半)。另一方面, 2 ∣ p 2\mid p 2p 时, l c m \rm lcm lcm 过程中百分之百会丢失至少一个 2 2 2,所以整个数字也在缩小到原来的一半。而 2 ≠ p 2\ne p 2=p 肯定有 2 ∣ p ′ 2\mid p' 2p,杠杠的!

总结起来就是:变大是不稳定且困难的,变小是容易且稳定的。我们有理由相信, p p p 不会大的离谱。

接下来,这道题就变成了玄学优化题。请洒潘江,各倾陆海云尔!

代码

最有用的代码是这份打表代码。它帮助我找到了结论。

	int p = 25; // whatever you want
	for(int i=2,g0=0,g1=1; true; ++i){
		g0 = (3ll*g1-g0+p)%p; swap(g0,g1);
		if(g0 == 0 && g1 == 1){
			printf("period = %d",i-1);
			break;
		}
	}

A C AC AC 代码反而挺无趣的……

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define __int128 long long
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(__int128 x){
	if(x > 9) writeint(x/10);
	putchar((x-x/10*10)^48);
}

const int MaxN = 1000002;
vector< int > primes;
int least[MaxN], next_one[MaxN], num[MaxN];
bool isPrime[MaxN]; int period[MaxN];
void sieve(int n = MaxN-1){
	memset(isPrime+2,1,n-1);
	for(int i=2,len=0; i<=n; ++i){
		if(isPrime[i]){
			++ len, primes.push_back(i);
			least[i] = i, num[i] = 1;
			next_one[i] = 1; // end
		}
		for(int j=0; j<len&&primes[j]<=n/i; ++j){
			isPrime[i*primes[j]] = false;
			least[i*primes[j]] = primes[j];
			if(i%primes[j] == 0){
				num[i*primes[j]] = num[i]+1;
				next_one[i*primes[j]] = next_one[i];
				break; // fenjie can be unordered
			}
			num[i*primes[j]] = 1;
			next_one[i*primes[j]] = i;
		}
	}
}

__int128 Mod; // vary
inline __int128 mul(__int128 a,__int128 b){
	a = (a*b-Mod*(__int128)((long double)a/Mod*b+1e-10))%Mod;
	return (a < 0) ? a+Mod : a; // avoid negative
}
struct Matrix{
	__int128 a[2][2];
	Matrix operator * (const Matrix &b) const {
		Matrix c; // no need to clear
		rep(i,0,1) rep(k,0,1) // manual
			c.a[i][k] = (mul(a[i][0],b.a[0][k])
				+mul(a[i][1],b.a[1][k]))%Mod;
		return c;
	}
};
Matrix qkpow(Matrix b,__int128 q){
	Matrix a = b; -- q;
	for(; q; q>>=1,b=b*b)
		if(q&1) a = a*b;
	return a;
}
__int128 qkpow(__int128 b,int q){
	__int128 a = 1;
	for(; q; q>>=1,b=mul(b,b))
		if(q&1) a = mul(a,b);
	return a;
}

Matrix Q, S; int A, B;
__int128 calc(__int128 n,const __int128 &p){
	if(n <= 1) return n ? B : A;
	else Mod = p; // setup
	/* [ g_{i-1},g_i ] * S = [ g_i,g_{i+1} ] */ ;
	S.a[0][0] = 0, S.a[1][0] = 1;
	S.a[0][1] = p-1, S.a[1][1] = 3;
	S = qkpow(S,n-1); // calculate it
	Q.a[0][0] = A%p, Q.a[0][1] = B%p;
	Q.a[1][0] = Q.a[1][1] = 0; Q = Q*S;
	return Q.a[0][1];
}

bool __switch_of_period;
__int128 getPeriod(const __int128 &p){
	if(p == 5) return 10; // the only exception
	if(p%5 == 1 || p%5 == 4) return p-1;
	else return p+1; // only two cases
	if(__switch_of_period){ // enabled
		int rnk = lower_bound(primes.begin(),
			primes.end(),p)-primes.begin();
		if((rnk^primes.size()) && primes[rnk] == p)
			return period[rnk]; // solved
	}
	__int128 gp_1 = calc(p-1,p), gp = calc(p,p);
	if(gp_1 == A%p && gp == B%p) return p-1;
	else return p+1; // believe in conclusion
}

bool miller_rabbin(const int &p){
	if(p <= 7) return p == 2 or
		p == 3 or p == 5 or p == 7;
	return (Mod = p) != 1
		and qkpow(2,p-1) == 1
		and qkpow(3,p-1) == 1
		and qkpow(5,p-1) == 1
		and qkpow(7,p-1) == 1;
}

map< int,int > cur; vector<int> sy;
typedef map< int,int >::iterator ddq;
void getFenJie(int v){
	if(miller_rabbin(v)) return void(++ cur[v]);
	for(int i=0,t; primes[i]<=v/primes[i]&&v>=MaxN; ++i)
		if(v%primes[i] == 0){
			for(t=0; !(v%primes[i]); ++t)
				v /= primes[i];
			cur[primes[i]] += t;
		}
	if(v >= MaxN) return void(++ cur[v]);
	for(; v!=1; v=next_one[v])
		cur[least[v]] += num[v];
}
__int128 hardCalc(__int128 p){
	if(miller_rabbin(p)) return getPeriod(p);
	__int128 res = 1, v; // make lcm right
	for(int i=0; primes[i]<=p/primes[i]&&p>=MaxN; ++i)
		if(p%primes[i] == 0){
			for(v=1; !(p%primes[i]); v*=primes[i])
				p /= primes[i];
			v = v/primes[i]*getPeriod(primes[i]);
			res = res/__gcd(res,v)*v;
		}
	if(p >= MaxN) return res*getPeriod(p);
	Mod = p; // whatever... big is enough
	for(; p!=1; p=next_one[p]){
		v = qkpow(least[p],num[p]-1);
		v *= getPeriod(least[p]);
		res = res/__gcd(res,v)*v;
	}
	return res;
}

const int Threshold = MaxN;
unordered_map< __int128,__int128 > path;
__int128 getG(int n,int k,__int128 p){
	sy.clear(); if(!k) return n%p; // exit
	if(path.count(p)) // recorded
		return calc(getG(n,k-1,path[p]),p);
	__int128 phi = p; ddq it;
	if(!cur.empty() && cur.rbegin()->first < Threshold){
		phi = hardCalc(p); goto FUCK_IT_ALL;
	}
	for(it=cur.begin(); it!=cur.end(); ){
		if(it->first != 5) // 5 won't fade
			phi /= it->first, -- it->second;
		sy.push_back(it->first); // record
		if(!it->second) cur.erase(it ++);
		else ++ it; // move ahead
	}
	for(int i=0,zxy=sy.size(); i<zxy; ++i){
		__int128 v = getPeriod(sy[i]);
		v /= __gcd(phi,v); phi *= v; // lcm
		getFenJie(v); // add it
	}
FUCK_IT_ALL:
	path[p] = phi; // memorize
	return calc(getG(n,k-1,phi),p);
}

int main(){
	freopen("hakugai.in","r",stdin);
	freopen("hakugai.out","w",stdout);
	sieve(); int len = primes.size();
	A = 0, B = 1; // whatever value
	rep(i,0,len-1) // prepare period
		period[i] = getPeriod(primes[i]);
	__switch_of_period = true;
	for(int T=readint(); T; --T){
		A = readint(), B = readint();
		int n = readint(), k = readint();
		int p = readint(); // stupid!!!
		cur.clear(); getFenJie(p);
		writeint(getG(n,k,p)), putchar('\n');
	}
	return 0;
}
/*
2
161412041 518937363 163320992 2 657844984
33527462 107256621 442389778 1 311584514

Ans: 78179392 217296843
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值