╰( ̄▽ ̄)╭
小 W喜欢读 书,尤其喜欢读 书,尤其喜欢读《约翰克里斯 朵夫》。 最近小 W准备读一本新书,这本一共有 p页, 页码范围为 0..p -1。
小 W很忙,所以每天只能读一页书 。为了使事情有趣一些 ,他打算使用 NOI2012上学习的线性同余法生成 一个序列 ,来决定每天具体读哪一页 。
我们用 Xi来表示通过这种方法生成出来第 i个数 ,也即小 W第 i天会读 哪一页 。这个方法 需要设置 3个参数 a,b,X1,满足 0≤a,b,X1≤p-1,且 a, b,X1都是整数 。按照下面的公式 按照下面的公式生成出来一系列的 整数。
其中 mod p 表示前面的数除以 p的余数。
可以发现,这个序列中下一个数总是由上一个数生成的 ,而且每一项都在 0..p -1这个范围内 ,是一个合法的页码。 同时需要注意 ,这种方法有可能导致某两天读的页码完全一样 。
小 W非常急切 地想去读这本书的第t页。所以他想知道, 对于一组给定的 a, b,X1,如果使用线性同余法来生成每一天读的页码, 最早读到第t页是在哪一天,或者指出他永远不会读到第t页。
(⊙ ▽ ⊙)
容易求出
设
则
特殊情况的处理:
在此之前我们必须保证
所以我们对于
同时我们也得保证
于是对于
此时,题目要求的是:
移项可得:
特殊情况的处理:
在此之前,我们还要保证
所以对于
令
的最小正整数解。(关于
于是就能套大步小步算法(BSGS algorithm)了。
( ̄~ ̄)
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<math.h>
#define ll long long
using namespace std;
const char* fin="jzoj3211.in";
const char* fout="jzoj3211.out";
const ll inf=0x7fffffff;
const ll maxh=1000007;
ll t,i,j,k,A,B,X1,n,mo;
ll h[maxh],minx[maxh];
bool noans;
ll hash(ll v){
ll k=v%maxh;
while (h[k] && h[k]!=v) k=(k+1)%maxh;
return k;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if (b==0){
x=1;
y=0;
return a;
}
ll r=exgcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-a/b*y;
return r;
}
ll qpower(ll a,ll b){
ll c=1;
while (b){
if (b&1) c=c*a%mo;
a=a*a%mo;
b>>=1;
}
return c;
}
int main(){
freopen(fin,"r",stdin);
freopen(fout,"w",stdout);
scanf("%lld",&t);
while (t--){
scanf("%lld%lld%lld%lld%lld",&mo,&A,&B,&X1,&n);
if (A==0){
if (X1==n) printf("1
");
else if (B==n) printf("2
");
else printf("-1
");
}else if (A==1){
ll x,y;
k=exgcd(mo,B,x,y);
if ((n-X1)%k) printf("-1
");
else{
x*=(n-X1)/k;
y*=(n-X1)/k;
y=(y%(mo/k)+(mo/k))%(mo/k);
printf("%lld
",y+1);
}
}else if (B==0 && X1==0){
if (X1==n) printf("1
");
else printf("-1
");
}else{
ll ks=(ll)sqrt(mo),tmp=qpower(A,ks),tmd=qpower(tmp,mo-2);
memset(h,0,sizeof(h));
memset(minx,0,sizeof(minx));
j=1;
for (i=0;i<ks;i++){
k=hash(j);
h[k]=j;
if (!minx[k] || minx[k]>i) minx[k]=i;
j=j*A%mo;
}
ll C=B*qpower(A-1,mo-2)%mo;
j=(n+C)*qpower(X1+C,mo-2)%mo;
noans=true;
for (i=0;i<=ks;i++){
k=hash(j);
if (h[k]){
ll ans=minx[k]+i*ks+1;
printf("%lld
",ans);
noans=false;
break;
}
j=j*tmd%mo;
}
if (noans) printf("-1
");
}
}
return 0;
}
(⊙v⊙)
对于一个有递推式的问题,可以考虑先求出通项公式,在对其因式分解。