1. 程式人生 > >BZOJ_P1467/POJ_P3243 Clever Y(擴充套件BSGS+雜湊)

BZOJ_P1467/POJ_P3243 Clever Y(擴充套件BSGS+雜湊)

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;
}