[ACNOI2022]数学压轴

280 篇文章 1 订阅

题目

题目背景
“卷爷,去看下 T 3 T3 T3 吧。 ”

“哼,我叫你看 T 3 T3 T3 的时候你怎么不去看?”

O n e I n D a r k \sf OneInDark OneInDark 啼笑皆非。“因为你是最强的啊,你要有最强者的担当!”

O L  ⁣ I Y E \sf OL\!IYE OLIYE 睁开了双眼,印着直巴的纹,蕴着血红的底。获得这双眼睛的人,都曾经历过杀戮。

“我的想法很简单:谁敢润我,我就把他卷飞——这样,你们应该能够稍微体会我的孤独与愤怒了吧!”

题目描述
递推数列 f n f_n fn 由如下方程给出: f 0 = f 1 = 1 ,    f n = 9 f n − 1 + 12 f n − 2    ( n ⩾ 2 ) f_0=f_1=1,\;f_n=9f_{n-1}+12f_{n-2}\;(n\geqslant 2) f0=f1=1,fn=9fn1+12fn2(n2)

现有 T T T 次询问,每次给定 a , b , c , d , P a,b,c,d,P a,b,c,d,P,请求出 gcd ⁡ ( a f n + b f n + 1 , c f n + d f n + 1 )   m o d   P \gcd(af_n{+}bf_{n+1},cf_n{+}df_{n+1})\bmod P gcd(afn+bfn+1,cfn+dfn+1)modP

数据范围与约定
数据组数 T ⩽ 1 0 5 T\leqslant 10^5 T105,而 n ⩽ 1 0 9 n\leqslant 10^9 n109 。还保证 P ⩽ 1 0 9 + 7 P\leqslant 10^9{+}7 P109+7 以及 a , b , c , d ∈ [ 0 , 1 0 3 ] a,b,c,d\in[0,10^3] a,b,c,d[0,103]

思路

100 % 100\% 100% 的数学题啊。只怪我数学太差。

首先我们至少需要会解决 gcd ⁡ ( f n , f n + 1 ) \gcd(f_n,f_{n+1}) gcd(fn,fn+1) 吧。然而就在这一步,我便停滞不前了!更可悲可叹的是,强如 O L  ⁣ I Y E \sf OL\!IYE OLIYE 者早已发现,其有固定结论
ρ n = gcd ⁡ ( f n , f n + 1 ) = 3 ⌊ n 2 ⌋ \rho_n=\gcd(f_n,f_{n+1})=3^{\lfloor{n\over 2}\rfloor} ρn=gcd(fn,fn+1)=32n

证明则可使用归纳法。在此之前,先说明

引理 f n f_n fn 3 3 3 的次数为 κ n = ⌊ n 2 ⌋ \kappa_n=\lfloor{n\over 2}\rfloor κn=2n,即 3 κ n ∣ f n 3^{\kappa_n}\mid f_n 3κnfn 3 κ n + 1 ∤ f n 3^{\kappa_n+1}\nmid f_n 3κn+1fn

证明:归纳法。首先 3 κ n ∣ f n 3^{\kappa_n}\mid f_n 3κnfn 显然,因为系数 9 , 12 9,12 9,12 都是 3 3 3 的倍数。而 f n ≡ 12 f n − 2 ( m o d 3 κ n + 1 ) f_n\equiv 12f_{n-2}\pmod{3^{\kappa_n+1}} fn12fn2(mod3κn+1),因为 κ n − 1 + 2 ⩾ κ n + 1 \kappa_{n-1}+2\geqslant \kappa_n+1 κn1+2κn+1 。由归纳法 12 f n − 2 12f_{n-2} 12fn2 3 3 3 的次数为 κ n − 2 + 1 = κ n \kappa_{n-2}+1=\kappa_n κn2+1=κn,所以 3 κ n + 1 ∤ f n 3^{\kappa_n+1}\nmid f_n 3κn+1fn 得证。

2 ∤ f n 2\nmid f_n 2fn 是显然的。于是 ρ n = gcd ⁡ ( f n , 12 f n − 1 ) = gcd ⁡ ( f n , 3 f n − 1 ) = 3 [ κ n > κ n − 1 ] ⋅ ρ n − 1 = 3 κ n \rho_n=\gcd(f_n,12f_{n-1})=\gcd(f_n,3f_{n-1})=3^{[\kappa_n>\kappa_{n-1}]}\cdot\rho_{n-1}=3^{\kappa_n} ρn=gcd(fn,12fn1)=gcd(fn,3fn1)=3[κn>κn1]ρn1=3κn,得证。

总算搞定了这个超级弱化版问题。考虑原问题,通过辗转相减总可以使得 d = 0 d=0 d=0 。所以我们实际上只需解决 gcd ⁡ ( a f n + b f n + 1 , c f n ) \gcd(af_n{+}bf_{n+1},cf_n) gcd(afn+bfn+1,cfn)

考虑到这个答案可能很大,想要求出,必然是半道上取模。也就是说将该 gcd ⁡ \gcd gcd 表为若干乘积。可以看到 c c c 孤零零的,那么我们可以把 c c c 提出来考虑。

要么先求 gcd ⁡ ( … , c ) \gcd(\dots,c) gcd(,c),然后 … \dots 部分除以该值,再与 f n f_n fn gcd ⁡ \gcd gcd 。要么先与 f n f_n fn gcd ⁡ \gcd gcd,剩余部分直接与 c c c gcd ⁡ \gcd gcd 。前者对问题的简化的帮助不大;选择后者,先求出
g = gcd ⁡ ( a f n + b f n + 1 , f n ) = gcd ⁡ ( b f n + 1 , f n ) = ρ n gcd ⁡ ( b , f n ρ n ) g=\gcd(af_n{+}bf_{n+1},f_n)=\gcd(bf_{n+1},f_n)=\rho_n\gcd\left(b,{f_n\over\rho_n}\right) g=gcd(afn+bfn+1,fn)=gcd(bfn+1,fn)=ρngcd(b,ρnfn)

最后一步的思想,跟目前求 gcd ⁡ ( ⋯   , c f n ) \gcd(\cdots,cf_n) gcd(,cfn) 一样:先计算 f f f,就会剩下一个含常数的 gcd ⁡ \gcd gcd 式。算 f n ρ n   m o d   b {f_n\over\rho_n}\bmod b ρnfnmodb 可以用矩阵加速,过程中将 3 3 3 除掉即可,因为转移矩阵的平方满足每一项都是 3 3 3 的倍数。

然后再求
gcd ⁡ ( a f n + b f n + 1 g , c ) \gcd\left(\frac{af_n{+}bf_{n+1}}{g},c\right) gcd(gafn+bfn+1,c)

这个看上去没法搞了。巧妙的是, ρ n ∣ g \rho_n\mid g ρng,于是左式就等于
( a f n ρ n + b f n + 1 ρ n ) [ gcd ⁡ ( b , f n ρ n ) ] − 1 \left(a\frac{f_n}{\rho_n}+b\frac{f_{n+1}}{\rho_n}\right)\left[\gcd\left(b,\frac{f_n}{\rho_n}\right)\right]^{-1} (aρnfn+bρnfn+1)[gcd(b,ρnfn)]1

然后对 c c c 取模,分子就只需要对 c gcd ⁡ ( b , f n ρ n ) c\gcd(b,\frac{f_n}{\rho_n}) cgcd(b,ρnfn) 取模。这是可接受的了。计算方法不变,时间复杂度 O ( T log ⁡ n ) \mathcal O(T\log n) O(Tlogn)

代码

#include <cstdio> // megalomaniac JZM yydJUNK!!!
#include <iostream> // Almighty XJX yyds!!!
#include <algorithm> // decent XYX yydLONELY!!!
#include <cstring> // Pet DDG yydOLDGOD!!!
#include <cctype> // oracle: ZXY yydBUSlut!!!
typedef long long llong;
# 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 rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
	return a*f;
}
inline int getGcd(int a, int b){
	for(; b; a%=b,a^=b^=a^=b){}; return a;
}

struct Matrix{ int a[2][2]; };
Matrix mat_mul(const Matrix &a, const Matrix &b, const int &mod){
	Matrix c; rep(i,0,1) rep(j,0,1) // use for loop
	c.a[i][j] = int((llong(a.a[i][0])*b.a[0][j]
		+llong(a.a[i][1])*b.a[1][j])%mod);
	return c; // hope that it's fast enough
}
Matrix qkpow(Matrix b, int q, const int &mod){
	if(!q) return Matrix{1,0,0,1};
	Matrix a = b; -- q; // avoid unit Matrix
	for(; q; q>>=1,b=mat_mul(b,b,mod))
		if(q&1) a = mat_mul(a,b,mod);
	return a;
}
inline llong qkpow(llong b, int q, const int &mod){
	llong a = 1;
	for(; q; q>>=1,b=b*b%mod) if(q&1) a = a*b%mod;
	return a;
}

Matrix norm; int coe[2][2];
int main(){
	norm.a[0][1] = 1, norm.a[1][0] = 12, norm.a[1][1] = 9;
	Matrix trans = mat_mul(norm,norm,1e9+7);
	rep(i,0,1) rep(j,0,1) trans.a[i][j] /= 3;
	for(int T=readint(); T; --T){
		int n = readint(), mod = readint();
		rep(i,0,1) rep(j,0,1) coe[i][j] = readint();
		for(int f; coe[1][1]; std::swap(coe[0],coe[1])){
			f = coe[0][1]/coe[1][1]; // ratios
			rep(i,0,1) coe[0][i] -= f*coe[1][i];
		}
		if(!coe[1][0]){ // just the other one
			Matrix ans = qkpow(norm,n,mod);
			int fn = ans.a[0][0]+ans.a[0][1];
			int fn_1 = ans.a[1][0]+ans.a[1][1];
			printf("%lld\n",(llong(fn_1)*coe[0][1]
				+llong(fn)*coe[0][0])%mod); continue;
		}
		if(coe[1][0] < 0) coe[1][0] = -coe[1][0];
		coe[0][0] = coe[0][0]%coe[1][0]+coe[1][0];
		if(coe[0][1] == 0){
			Matrix ans = qkpow(norm,n,mod);
			llong fn = ans.a[0][0]+ans.a[0][1]; // get this
			printf("%lld\n",getGcd(coe[0][0],
				coe[1][0])*fn%mod); continue;
		}
		Matrix tmp = qkpow(trans,n>>1,coe[0][1]); // [2m,2m+1]
		int bgcd = tmp.a[n&1][0]+tmp.a[n&1][1];
		bgcd = getGcd(bgcd,coe[0][1]); // part of g
		int cmod = coe[1][0]*bgcd; // modules
		tmp = qkpow(trans,n>>1,cmod); // [2m,2m+1]
		int frhon = tmp.a[n&1][0]+tmp.a[n&1][1], frhon_1;
		if(n&1) frhon_1 = int((llong(9)*frhon+
			llong(12)*(tmp.a[0][0]+tmp.a[0][1]))%cmod);
		else frhon_1 = tmp.a[1][0]+tmp.a[1][1];
		int cgcd = int((llong(coe[0][0])*frhon
			+llong(coe[0][1])*frhon_1)%cmod);
		cgcd = getGcd(cgcd/bgcd,coe[1][0]);
		printf("%lld\n",qkpow(3,n>>1,mod)*bgcd%mod*cgcd%mod);
	}
	return 0;
}

后记

我本来想构建模 p k p^k pk 的域,但我失败了。形式幂级数?形式洛朗级数?好像都不行。拜托,如果这种东西真的存在,那肯定早就被提出与使用了;用得着我吗!

我去问数竞同学 Y J \sf YJ YJ,他表示他们不会研究这种东西,只要 “能在有限步内结束” 就 O K \rm OK OK 了。也就是说他也没看出来三的次幂的结论

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值