【bzoj4002】[JLOI2015]有意义的字符串【数论】【矩阵快速幂】

题意:求 ⌊ ( b + d 2 ) n ⌋   m o d   7528443412579576937 \lfloor(\frac{b+\sqrt{d}}{2})^n\rfloor\ mod\ 7528443412579576937 (2b+d )n mod 7528443412579576937的值。
题解:get到了好多的新姿势qwq。
首先get到了特征多项式相关姿势。orz zjr and yeh dark♂佬,直接秒了特征多项式。
我们设有一个数列 x x x,满足 x n + 2 = c 1 x n + 1 + c 2 x n x_{n+2}=c_{1}x_{n+1}+c_{2}x_{n} xn+2=c1xn+1+c2xn
我们设存在 r r r s s s,满足 x n + 2 − r x n + 1 = s ( x n + 1 − r x n ) x_{n+2}-rx_{n+1}=s(x_{n+1}-rx_{n}) xn+2rxn+1=s(xn+1rxn)
移项得 x n + 2 = ( s + r ) x n + 1 − s r × x n x_{n+2}=(s+r)x_{n+1}-sr\times x_{n} xn+2=(s+r)xn+1sr×xn
可以得到 c 1 = s + r   ( 1 ) c_{1}=s+r\ (1) c1=s+r (1) c 2 = − s r   ( 2 ) c_{2}=-sr\ (2) c2=sr (2)
我们让 ( 1 ) × r + ( 2 ) (1)\times r+(2) (1)×r+(2),得到 r c 1 + c 2 = r 2 rc_{1}+c_{2}=r^2 rc1+c2=r2
这就是一个一元二次方程,可以直接解出。
我们设这个方程的两个根为 p , q p,q p,q
存在 A , B A,B A,B使得 x n = A × p n + B × q n x_{n}=A\times p^{n}+B\times q^{n} xn=A×pn+B×qn
只需要带入两个 n n n的值,就可以把 A A A B B B求出,也就是得出了通项公式。
我们现在要求的就是 ( b + d 2 ) n (\frac{b+\sqrt{d}}{2})^n (2b+d )n
我们构造一个式子 ( ( b + d 2 ) n + ( b − d 2 ) n ) − ( b − d 2 ) n ((\frac{b+\sqrt{d}}{2})^n+(\frac{b-\sqrt{d}}{2})^n)-(\frac{b-\sqrt{d}}{2})^n ((2b+d )n+(2bd )n)(2bd )n
前面这些东西 ( ( b + d 2 ) n + ( b − d 2 ) n ) ((\frac{b+\sqrt{d}}{2})^n+(\frac{b-\sqrt{d}}{2})^n) ((2b+d )n+(2bd )n),我们可以把这个看成一个递推式的通项,得到 A = 1 , B = 1 , p = b + d 2 , q = b − d 2 A=1,B=1,p=\frac{b+\sqrt{d}}{2},q=\frac{b-\sqrt{d}}{2} A=1,B=1,p=2b+d ,q=2bd 。所以现在我们要做的就是由通项求出递推式。
因为 p , q p,q p,q是方程 r c 1 + c 2 = r 2 rc_{1}+c_{2}=r^2 rc1+c2=r2的两个根,我们可以尝试把他们带入,得到
( b + d 2 ) 2 = c 1 × ( b + d 2 ) + c 2 (3) (\frac{b+\sqrt d}{2})^2=c_{1}\times(\frac{b+\sqrt d}{2})+c_{2}\tag{3} (2b+d )2=c1×(2b+d )+c2(3)
( b − d 2 ) 2 = c 1 × ( b − d 2 ) + c 2 (4) (\frac{b-\sqrt d}{2})^2=c_{1}\times(\frac{b-\sqrt d}{2})+c_{2}\tag{4} (2bd )2=c1×(2bd )+c2(4)

化简得
b 2 + 2 b d + d 4 = c 1 b + c 1 d 2 + c 2 (5) \frac{b^2+2b\sqrt d+d}{4}=\frac{c_{1}b+c_{1}\sqrt d}{2}+c_{2}\tag{5} 4b2+2bd +d=2c1b+c1d +c2(5)
b 2 − 2 b d + d 4 = c 1 b − c 1 d 2 + c 2 (6) \frac{b^2-2b\sqrt d+d}{4}=\frac{c_{1}b-c_{1}\sqrt d}{2}+c_{2}\tag{6} 4b22bd +d=2c1bc1d +c2(6)

( 3 ) + ( 4 ) (3)+(4) (3)+(4)
b 2 + d 2 = c 1 b + 2 c 2 (7) \frac{b^2+d}{2}=c_{1}b+2c_{2}\tag{7} 2b2+d=c1b+2c2(7)

( 3 ) − ( 4 ) (3)-(4) (3)(4) c 1 = b c_{1}=b c1=b。带入 ( 7 ) (7) (7)得到 c 2 = d − b 2 4 c_{2}=\frac{d-b^2}{4} c2=4db2
于是我们成功推出了递推式:
x n = b × x n − 1 + d − b 2 4 × x n − 2 x_{n}=b\times x_{n-1}+\frac{d-b^2}{4}\times x_{n-2} xn=b×xn1+4db2×xn2
很容易得到边界 x 0 = 2 , x 1 = b x_{0}=2,x_{1}=b x0=2,x1=b
注意到 b ≡ 1   m o d   2 b\equiv1\ mod\ 2 b1 mod 2 d ≡ 1   m o d   4 d\equiv1\ mod\ 4 d1 mod 4,所以 ( b + d 2 ) n + ( b − d 2 ) n (\frac{b+\sqrt{d}}{2})^n+(\frac{b-\sqrt{d}}{2})^n (2b+d )n+(2bd )n一定是整数。所以我们可以直接跑整数的矩阵乘法。
注意到 b 2 ≤ d < ( b + 1 ) 2 b^2\leq d<(b+1)^2 b2d<(b+1)2,所以 ( b − d 2 ) n (\frac{b-\sqrt{d}}{2})^n (2bd )n一定在 ( − 1 , 0 ] (-1,0] (1,0]内。 ( b − d 2 ) n (\frac{b-\sqrt{d}}{2})^n (2bd )n对答案有影响,当且仅当 b × b ≠ d b\times b\ne d b×b=d n n n为偶数。这时答案要减1。
注意模数很恶心,一加都爆 l o n g   l o n g long\ long long long,不仅要用 O ( 1 ) O(1) O(1)快速乘,还要用 u n s i g n e d   l o n g   l o n g unsigned\ long\ long unsigned long long
代码


#include<cstdio>
#include<cstring>
#include<cmath>
#define int unsigned long long
const int mod=7528443412579576937;
int b,d,n,k,a[2][2],base[2][2],tmp[2][2];
int mul(int a,int b){
	long long c=a*(long double)b/mod+0.5;
	long long res=a*b-c*mod;
	if(res<0){
		res+=mod;
	}
	return res;
}
int fastpow(int a,int x){
	int res=1;
	while(x){
		if(x&1){
			res=mul(res,a);
		}
		x>>=1;
		a=mul(a,a);
	}
	return res;
}
void mul(int a[][2],int b[][2],int c[][2]){
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			for(int k=0;k<2;k++){
				tmp[i][j]=(tmp[i][j]+mul(a[i][k],b[k][j]))%mod;
			}
		}
	}
	memcpy(c,tmp,sizeof(tmp));
}
signed main(){
	scanf("%lld%lld%lld",&b,&d,&n);
	if(!n){
		puts("1");
		return 0;
	}else if(n==1){
		printf("%lld\n",(int)(0.5*(b+sqrt(d))));
		return 0;
	}
	a[0][0]=2;
	a[0][1]=b;
	base[0][1]=(d-b*b)/4;
	base[1][0]=1;
	base[1][1]=b;
	int k=n;
	while(k){
		if(k&1){
			mul(a,base,a);
		}
		k>>=1;
		mul(base,base,base);
	}
	if(n%2==0&&b*b!=d){
		printf("%lld\n",a[0][0]-1);
	}else{
		printf("%lld\n",a[0][0]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值