bzoj3122 随机数生成器 BSGS

       这道题目真是太坑爹了。。。狂Wa无数次。

       将原来那个看成X0,最后答案再加1,这样会简单一点,假设答案是n(最后需要+1),那么不断迭代会发现

       Xn=A^nX0+(A^n-1+A^n-2+...+1)B≡t(mod p),然后运用等比数列公式得到A^nX0+(A^n-1)/(A-1) *B≡t(mod p),两边同乘(A-1),移项得到:

       [(a-1)*X0+b] A^nt(a-1)+b(mod p),然后就可以运用逆把 A^n求出来,然后就可以运用BSGS(Baby-Step-Giant-Step,Shank大步小步法)得到n了。

       注意上述公式再a=0,a=1时失效,需要特判。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;

int p,a,b,x,t,n,cnt; struct node{ int x,y; }c[100005];
int inv(int x){
	int i,sum=1;
	for (i=p-2; i; i>>=1,x=(ll)x*x%p) if (i&1) sum=(ll)sum*x%p;
	return sum;
}
bool cmp(node aa,node bb){ return (aa.x==bb.x)?aa.y<bb.y:aa.x<bb.x; }
int find(int v){
	int l=1,r=n,mid;
	while (l<r){
		mid=(l+r)>>1; if (c[mid].x<v) l=mid+1; else r=mid;
	}
	return (c[l].x==v)?c[l].y:-1;
}
int bsgs(int u,int v){
	int i,tmp=1; n=(int)sqrt(p)+1;
	c[1].x=1; c[1].y=1;
	for (i=2; i<=n; i++){ c[i].x=(ll)c[i-1].x*u%p; c[i].y=i; }
	tmp=inv((ll)c[n].x*u%p);
	sort(c+1,c+n+1,cmp);	
	for (i=0; i<n; i++){
		int k=find(v);	if (k!=-1) return i*n+k;
		v=(ll)v*tmp%p;
	}
	return -1;
}
int solve(){
	if (x==t) return 1;
	if (!a) return (b==t)?2:-1;
	if (a==1) return (b)?(ll)inv(b)*(t-x+p)%p+1:-1;
	int fz=((ll)t*(a-1)%p+b)%p,fm=((ll)x*(a-1)%p+b)%p;
	return bsgs(a,(ll)fz*inv(fm)%p);
}
int main(){
	int cas; scanf("%d",&cas);
	while (cas--){
		scanf("%d%d%d%d%d",&p,&a,&b,&x,&t);
		printf("%d\n",solve());
	}
	return 0;
}

by lych
2016.2.15

你可能感兴趣的:(数论,逆,等比数列,BSGS)