BZOJ3122 SDOI2013 随机数生成器 BSGS

做完之后的结论就是:果然考一次数论就要把一片东西全考了。

等比数列是什么?虐爆高一狗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;
}


你可能感兴趣的:(BZOJ3122 SDOI2013 随机数生成器 BSGS)