BZOJ_P1467/POJ_P3243 Clever Y(擴充套件BSGS+雜湊)
阿新 • • 發佈:2019-02-09
Time Limit: 4 Sec Memory Limit: 64 MB
Submit: 202 Solved: 106
[Submit][Status][Discuss]
Description
小Y發現,數學中有一個很有趣的式子: X^Y mod Z = K 給出X、Y、Z,我們都知道如何很快的計算K。但是如果給出X、Z、K,你是否知道如何快速的計算Y呢?
Input
本題由多組資料(不超過20組),每組測試資料包含一行三個整數X、Z、K(0 <= X, Z, K <= 109)。 輸入檔案一行由三個空格隔開的0結尾。
Output
對於每組資料:如果無解則輸出一行No Solution,否則輸出一行一個整數Y(0 <= Y < Z),使得其滿足XY mod Z = K,如果有多個解輸出最小的一個Y。
Sample Input
5 58 33
2 4 3
0 0 0
Sample Output
9
No Solution
HINT
Source
ghy
Sol:
擴充套件BSGS
兩種思路
a^x=b(mod p) =>x=i*m+j
a^x=b(mod p) =>x=i*m-j
都可以做,第一種要求逆元,加小範圍暴力,比較麻煩,第二種要快一些,第二種要考慮的東西比較多,還是比較好寫。
ps:手打Hash大法好!
這是第二種做法,把x拆成i*m-j
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<iostream>
using namespace std;
#define Mod 99991
typedef long long LL;
inline LL in(LL x=0,char ch=getchar(),int v=1){
while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();if(ch=='-') v=-1,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*v;
}
struct HashTable{LL k,v,nxt;}hash[Mod<<1];int head[Mod];int sum;
void Add(LL k,LL v){
int ha=k%Mod;
hash[++sum].nxt=head[ha],hash[sum].k=k,hash[sum].v=v;head[ha]=sum;
}
LL find(LL k){
int ha=k%Mod;
for(int i=head[ha];~i;i=hash[i].nxt) if(hash[i].k==k) return hash[i].v;
return -1;
}
LL pow(LL a,LL b,LL p,LL res=1){while(b){if(b&1) res=res*a%p;a=a*a%p,b>>=1;};return res;}
LL Exbsgs(LL a,LL b,LL p){
if(a%=p,b%=p,b==1) return 0;
LL tmp=1,d=1,cnt=0;
while((tmp=__gcd(a,p))!=1){
if(b%tmp) return -1;
cnt++,b/=tmp,p/=tmp,d=d*(a/tmp)%p;
if(b==d) return cnt;
}
LL m=ceil(sqrt(p)),j=b;sum=0;
memset(head,-1,sizeof(head));memset(hash,0,sizeof(hash));
for(LL i=0;i<m;i++) Add(j,i),j=j*a%p;tmp=pow(a,m,p);
for(LL i=1;i<=m+1;i++) if(d=d*tmp%p,~(j=find(d))) return i*m-j+cnt;
return -1;
}
int main(){
LL a,b,p,ans;
while(a=in(),p=in(),b=in(),a||b||p){
ans=Exbsgs(a,b,p);
if(~ans) printf("%lld\n",ans);else puts("No Solution");
}
return 0;
}
第一種做法:
x=i*m+j
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
#define Mod 99991
typedef long long LL;
inline LL in(LL x=0,char ch=getchar(),int v=1){
while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();if(ch=='-') v=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*v;
}
struct HashTable{LL k,v,nxt;}hash[Mod<<1];int head[Mod];int sum;
void Exgcd(LL a,LL b,LL &x,LL &y){if(!b){x=1,y=0;return;}Exgcd(b,a%b,y,x);y-=a/b*x;}
void Add(LL k,LL v){
int ha=k%Mod;
hash[++sum].nxt=head[ha],hash[sum].k=k,hash[sum].v=v;head[ha]=sum;
}
LL find(LL k){
int ha=k%Mod;
for(int i=head[ha];~i;i=hash[i].nxt) if(hash[i].k==k) return hash[i].v;
return -1;
}
LL Exbsgs(LL a,LL b,LL p){
LL tmp=1,d=1;LL cnt=0;
for(LL i=0;i<=100;i++){
if(tmp==b) return i;
tmp=tmp*a%p;
}
while((tmp=__gcd(a,p))!=1){
if(b%tmp) return -1;
cnt++,b/=tmp,p/=tmp;
d=d*a/tmp%p;
}
LL m=ceil(sqrt(p)),j=1;sum=0;
memset(head,-1,sizeof(head));memset(hash,0,sizeof(hash));
for(LL i=0;i<m;i++) Add(j,i),j=j*a%p;
LL x,y,k;tmp=j;
for(LL i=0;i<m;i++,d=d*tmp%p){
Exgcd(d,p,x,y);k=(x*b%p+p)%p,j=find(k);
if(~j) return (LL)i*m+j+cnt;
}
return -1;
}
int main(){
LL a,b,p,ans;
while(a=in(),p=in(),b=in(),a||b||p){
ans=Exbsgs(a,b,p);
if(~ans) printf("%lld\n",ans);else puts("No Solution");
}
return 0;
}