传送门
先说一下暴力的写法,这道题最好是写一个小暴力拍一下:
按照题目描述的枚举即可,但是要注意判断无解的条件。通过观察可以发现这个数列是存在循环节的,如果已经找到了循环节并且第一个循环节内没有满足条件的 xn 那么就可以直接退出啦。
正解的话应该是把这个式子变一下形然后做BSGS,推倒如下:
原式可以化为:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
#define LL long long
LL T,P,a,b,x1,t;
LL ny1,ny2,val1,val2,val3;
LL ans;
map <LL,LL> hash;
inline LL fast_pow(LL a,LL p){
LL ans=1;
for (;p;p>>=1,a=a*a%P)
if (p&1)
ans=ans*a%P;
return ans;
}
inline LL BSGS(LL a,LL b){
LL m=ceil(sqrt(P));
LL a_m=fast_pow(a,m);
hash.clear();
LL mul=1;
LL val=mul*b%P;
hash[val]=0;
for (LL j=1;j<=m;++j){
mul=mul*a%P;
val=mul*b%P;
hash[val]=j;
}
mul=1;
for (LL i=1;i<=m;++i){
mul=mul*a_m%P;
if (hash[mul]){
LL x=i*m-hash[mul];
return x+1;
}
}
return -1;
}
int main(){
scanf("%lld",&T);
while (T--){
scanf("%lld%lld%lld%lld%lld",&P,&a,&b,&x1,&t);
//一坨特判
if (t==x1){
printf("1\n");
continue;
}
if (a==0){
if (t==b) printf("2\n");
else printf("-1\n");
continue;
}
if (a==1&&b==0){
printf("-1\n");
continue;
}
if (a==1){
int nyb=fast_pow(b,P-2);
ans=((((t-x1)%P+P)%P)*nyb%P)%P;
printf("%lld\n",ans+1);
continue;
}
ny1=fast_pow(a-1,P-2);
val1=b*ny1%P;
val2=(x1%P+val1)%P;
ny2=fast_pow(val2,P-2);
val3=(t+val1)%P;
b=(val3*ny2)%P;
ans=BSGS(a,b);
printf("%lld\n",ans);
}
}
①注意负数的情况。
②先%后+还是先+后%想清楚。在纸上列列公式,实在不行先换元。
③在%p意义下a的逆元为 ap−2 ,ap互质且p为质数,不要搞错了。
④其实这道题是在式子的两边同时加上了一个常数,然后化成通项公式。也就是找出 X1 和 Xn 的关系。这样的话要保证 X1 和 Xn 的系数相等(把a提出),且可以一直推下去(也就是说保证式子左边的一坨和右边除去系数的一坨相同,则同时加上的常数可以计算出来)。