做完之后的结论就是:果然考一次数论就要把一片东西全考了。
等比数列是什么?虐爆高一狗QAQ
首先可以发现原来的递推式可以写成秦九韶的展开形式,就是说xn=a^(n-1)*x1+b*sum(a^i)(i=0->n-2)。运用等比数列求和公式得到xn=a^(n-1)*x1+b*(a^(n-1)-1)/(a-1) = t(mod p)。
由于要取余,因此我们先算出a-1的逆元等于c,代入后整理可以得到(x1+bc)*a^(n-1)=bc+t(mod p),先通过exgcd求出a^(n-1),然后BSGS求n即可。
注意几个特殊情况:
①、x1==t的时候n=1
②、a==0的时候看b是否等于t
③、a==1的时候原式可以化为t=x1+(n-1)b,exgcd求解不解释
以上三点才是本题最难的地方,不在难求,而在细心。
最后附代码:
#include<map> #include<cmath> #include<cstdio> #include<iostream> #include<algorithm> #define ll long long using namespace std; int T; ll p,a,b,x1,t; map<ll,int> table; map<ll,int>::iterator it; ll exgcd(ll a,ll b,ll &x,ll &y){ if(!b){ x=1;y=0; return a; } ll tmp=exgcd(b,a%b,x,y),t=x; x=y,y=t-a/b*y; return tmp; } ll Quick_pow(ll a,ll b,ll p){ a%=p; ll t=(b&1?a:1); while(b>>=1){ a*=a,a%=p; if(b&1) t*=a,t%=p; } return t; } ll bsgs(ll A,ll B,ll C){ table.clear(); A%=C; if(!A){ if(!B) return 1; else return -1; } ll m=ceil(sqrt(C)),t=1,tmp=1,x,y,ans=-1; for(int i=0;i<m;i++,t=(t*A)%p) table[t]=i; for(int i=0;i<m;i++){ exgcd(tmp,C,x,y); x=((x*B)%C+C)%C; it=table.find(x); if(it!=table.end()){ ans=i*m+it->second; break; } tmp=(tmp*t)%C; } if(ans==-1) return -1; return ans; } ll cal1(){ ll C=(t-x1+p)%p,x,y,t=exgcd(b,p,x,y); if(C%t) return -1; C/=t; x=x*C%p; if(x<0) x+=p; return x+1; } ll cal2(){ ll c=Quick_pow(a-1,p-2,p),A=(x1+b*c)%p,C=(t+b*c)%p,x,y,t=exgcd(A,p,x,y); if(C%t) return -1; C/=t; if(x<p) x=x%p+p; t=bsgs(a,x*C%p,p); if(t!=-1) return t+1; return -1; } ll solve(){ if(x1==t) return 1; if(!a){ if(b==t)return 2; return -1; } if(a==1) return cal1(); return cal2(); } int main(){ cin >> T; while(T--){ scanf("%d %d %d %d %d",&p,&a,&b,&x1,&t); cout << solve() << endl; } return 0; }