jzoj4015-数列【循环节,数论】

正题

题目链接:https://jzoj.net/senior/#contest/show/3011/0


题目大意

给出 n , m , a , b , c , x 0 n,m,a,b,c,x_0
x i = a x i 1 2 + b x i 1 + c x_i=ax_{i-1}^2+bx_{i-1}+c

x n % m x_n\%m


解题思路

第一段 n 1 e 6 n\leq 1e6 直接 O ( n ) O(n) 暴力做
第二段 m 1 e 6 m\leq 1e6 找到一个循环节然后在套到n里
第三段:
x i = a x i 1 2 + b x i 1 + c x_i=ax_{i-1}^2+bx_{i-1}+c
学过二次函数的对于给出的性质有敏锐的直觉
x i = a ( x + b 2 a ) 2 + 4 a c b 2 4 a x_i=a(x+\frac{b}{2a})^2+\frac{4ac-b^2}{4a}
4 a c = b 2 2 b 4 a c b 2 = 2 b 4ac=b^2-2b\Rightarrow 4ac-b^2=2b
x i = a ( x + b 2 a ) 2 b 2 a x_i=a(x+\frac{b}{2a})^2-\frac{b}{2a}
然后定义 k = b 2 a k=\frac{b}{2a}
x i + k = a ( x + k ) 2 x_i+k=a(x+k)^2
同时乘上 a a
a ( x i + k ) = ( a ( x + k ) ) 2 a(x_i+k)=(a(x+k))^2
定义 y i = a ( x i + k ) y_i=a(x_i+k)
那么有 y i = y i 1 2 y_i=y_{i-1}^2
y n = y 0 2 n y_n=y_{0}^{2^n}

用费马小可以让指数摸上 m 1 m-1 计算出 y n y_n ,然后倒推出 x n x_n


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll M=1e6+10;
ll n,m,a,b,c,x,fa[M],cir[M],v[M];
void solve1(){
	for(ll i=1;i<=n;i++)
		x=(a*x%m*x%m+b*x%m+c)%m;
	printf("%lld",x);
}
void solve2(){
	x%=m;
	for(ll i=0;i<=m;i++)
		fa[i]=(a*i%m*i%m+b*i%m+c)%m;
	ll cnt=0;cir[0]=x;v[x]=1;
	while(!v[x=fa[x]])
		cir[++cnt]=x,v[x]=cnt;
	x=v[x];
	if(n<=cnt) printf("%lld",cir[n]);
	else{n-=x;printf("%lld",cir[x+n%(cnt-x+1)]);}
}
ll power(ll x,ll b,ll p){
	ll ans=1;
	while(b){
		if(b&1) ans=ans*x%p;
		x=x*x%p;b>>=1;
	}
	return ans;
}
void solve3(){
	ll z=b/2/a,y=a*(x+z)%m;
	y=power(y,power(2,n,m-1),m);
	printf("%lld",(y*power(a,m-2,m)%m-z+m)%m);
}
int main()
{
	scanf("%lld%lld%lld%lld%lld%lld",&x,&a,&b,&c,&n,&m);
	x=x%m;a%=m;b%=m;c%=m;
	if(n<=1e6)solve1();
	else if(m<=1e6)solve2();
	else solve3();
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/104223363