【POJ3243】拓展BSGS(附hash版)

  上一篇博文中说道了baby step giant step的方法(简称BSGS),不过对于XY mod Z = K ,若x和z并不互质,则不能直接套用BSGS的方法了。

  为什么?因为这时候不存在逆元了啊,那么怎么办呢?

  既然是x和z不互质,那么我们就想办法让他们互质,再套用BSGS的解法即可。(这就是所谓的消因子法)

 

 

          

 

 

代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<cmath>
  7 using namespace std;
  8 #define LL long long
  9 #define Maxn 40000
 10 
 11 LL x,z,k,aa,m;
 12 int cnt,num;
 13 int ok;
 14 
 15 struct node
 16 {
 17     int idx;LL val;
 18 }baby[Maxn];
 19 
 20 LL ax,ay;
 21 LL exgcd(LL a,LL b)
 22 {
 23     if(b==0) {ax=1,ay=0;return a;}
 24     LL g=exgcd(b,a%b);
 25     LL yy=ay;
 26     ay=ax-a/b*ay;ax=yy;
 27     return g;
 28 }
 29 
 30 bool cmp(node x,node y) {return x.val==y.val?x.idx<y.idx:x.val<y.val;}
 31 
 32 int ffind(LL x)
 33 {
 34     int head=0,tail=cnt;
 35     while(head<tail)
 36     {
 37         int mid=(head+tail)>>1;
 38         if(baby[mid].val==x) return baby[mid].idx;
 39         if(baby[mid].val>x) tail=mid-1;
 40         else head=mid+1;
 41     }
 42     if(baby[head].val==x) return baby[head].idx;
 43     return -1;
 44 }
 45 
 46 bool init()
 47 {
 48     scanf("%lld%lld%lld",&x,&z,&k);
 49     //if(==EOF) return 0;
 50     if(x==0&&z==0&&k==0) return 0;k%=z;
 51     LL g,bm;
 52     bm=1%z;aa=1,num=0;ok=1;
 53     //if(k>=z) {ok=0;return 1;}
 54     for(int i=0;i<=100;i++) if(bm==k) {printf("%d\n",i);ok=0;return 1;}
 55     else bm=(bm*x)%z;
 56     while((g=exgcd(x,z))!=1)
 57     {
 58         aa=(aa*x/g)%z,z/=g;num++;
 59         if(k%g!=0) {ok=-1;break;}
 60         k/=g;
 61     }
 62     return 1;
 63 }
 64 
 65 LL BSGS()
 66 {
 67     baby[0].idx=0,baby[0].val=aa%z;
 68     m=(LL)(ceil(double(sqrt((double)z))));
 69     for(int i=1;i<=m;i++) baby[i].idx=i,baby[i].val=(baby[i-1].val*x)%z;
 70     LL bm=1%z,ans=-1,g;
 71     for(int i=1;i<=m;i++) bm=(bm*x)%z;
 72     g=exgcd(bm,z);
 73     bm=ax/g; bm=(bm%(z/g)+(z/g))%(z/g);
 74     if(bm==0) bm=z/g;
 75     sort(baby,baby+m+1,cmp);cnt=0;
 76     for(int i=1;i<=m;i++) if(baby[i].val!=baby[cnt].val) baby[++cnt]=baby[i];
 77     LL tmp=k;
 78     for(int i=0;i<=m;i++)
 79     {
 80         int j;
 81         if((j=ffind(tmp))!=-1)
 82         {
 83             ans=i*m+j;
 84             break;
 85         }
 86         tmp=(tmp*bm)%z;
 87     }
 88     return ans;
 89 }
 90 
 91 int main()
 92 {
 93     while(1)
 94     {
 95         LL ans;
 96         if(!init()) break;
 97         if(ok==0) continue;
 98         else if(ok==-1) printf("No Solution\n");
 99         else
100         {
101             ans=BSGS();
102             if(ans==-1) printf("No Solution\n");
103             else printf("%lld\n",ans+num);
104         }
105     }
106     return 0;
107 }
poj3243 (二分版)

 

 

  另外,find部分可以不用二分,而用hash解决。感觉大部分情况下还是hash比较快,但是比较耗空间。

  把你需要存的数,即x的0~m次方算出来,假设是t,我们设m=1<<16-1,然后用t异或^m得nt(就是取t二进制后的15位进行hash),然后存到hash表里面去。如果t的位置目前没有存数,那么我们就直接存到hash[t]上去,如果t位置已经存了数(因为后15位为t的可能有多种情况),我们就在len除增加一个位置,把nt存到那里面去,然后hash[t].next=len,把位置记录下来,这应该就相当于一条链了。查找的时候循着这条链找下去即可,链的尽头的next用-1标记。

 

  hash版代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<cmath>
  7 using namespace std;
  8 #define LL long long
  9 #define Maxn 40000
 10 const int pp=(1<<16)-1;
 11 
 12 LL x,z,k,aa,m;
 13 int cnt,num;
 14 int ok;
 15 
 16 struct node
 17 {
 18     int idx,nt;
 19     LL val;
 20 }baby[2*Maxn];int len;
 21 
 22 LL ax,ay;
 23 LL exgcd(LL a,LL b)
 24 {
 25     if(b==0) {ax=1,ay=0;return a;}
 26     LL g=exgcd(b,a%b);
 27     LL yy=ay;
 28     ay=ax-a/b*ay;ax=yy;
 29     return g;
 30 }
 31 
 32 void ins(LL now,int id)
 33 {
 34     int x=now&pp;
 35     if(baby[x].idx==-1) {baby[x].idx=id;baby[x].val=now;return;}
 36     while(baby[x].val!=now&&baby[x].nt!=-1) x=baby[x].nt;
 37     if(baby[x].val==now) return;
 38     baby[x].nt=++len;
 39     baby[len].nt=-1;baby[len].val=now;baby[len].idx=id;
 40 }
 41 
 42 bool init()
 43 {
 44     scanf("%lld%lld%lld",&x,&z,&k);
 45     
 46     if(x==0&&z==0&&k==0) return 0;k%=z;
 47     LL g,bm;
 48     bm=1%z;aa=1,num=0;ok=1;
 49     
 50     for(int i=0;i<=100;i++) if(bm==k) {printf("%d\n",i);ok=0;return 1;}
 51     else bm=(bm*x)%z;
 52     while((g=exgcd(x,z))!=1)
 53     {
 54         z/=g,aa=(aa*x/g)%z;num++;
 55         if(k%g!=0) {ok=-1;break;}
 56         k/=g;
 57     }
 58     
 59     return 1;
 60 }
 61 
 62 int ffind(LL now)
 63 {
 64     int x=now&pp;
 65     if(baby[x].idx==-1) return -1;
 66     while(baby[x].val!=now&&baby[x].nt!=-1) x=baby[x].nt;
 67     if(baby[x].val!=now) return -1;
 68     return baby[x].idx;
 69 }
 70 
 71 LL BSGS()
 72 {
 73     m=(LL)(ceil(double(sqrt((double)z))));
 74     
 75     
 76     for(int i=0;i<=pp;i++) baby[i].idx=baby[i].nt=-1;
 77     
 78     LL now=aa%z; len=pp;
 79     for(int i=0;i<=m;i++) {ins(now,i);now=(now*x)%z;}
 80     
 81     LL bm=1%z,ans=-1;
 82     for(int i=1;i<=m;i++) bm=(bm*x)%z;
 83     LL g=exgcd(bm,z);
 84     bm=ax/g; bm=(bm%(z/g)+(z/g))%(z/g);
 85     if(bm==0) bm=z/g;
 86     
 87     LL tmp=k;
 88     for(int i=0;i<=m;i++)
 89     {
 90         int j;
 91         if((j=ffind(tmp))!=-1)
 92         {
 93             ans=i*m+j;
 94             break;
 95         }
 96         tmp=(tmp*bm)%z;
 97     }
 98     return ans;
 99 }
100 
101 int main()
102 {
103     while(1)
104     {
105         LL ans;
106         if(!init()) break;
107         if(ok==0) continue;
108         else if(ok==-1) printf("No Solution\n");
109         else
110         {
111             ans=BSGS();
112             if(ans==-1) printf("No Solution\n");
113             else printf("%lld\n",ans+num);
114         }
115     }
116     return 0;
117 }
poj3243 (hash版)

 

 

  在我的理解中呢,hash相当于给每个数给予一个特征,这个特征的种类有限,而且我们根据一个数可以很快知道他的特征。我们存数的事后呢,就把相同特征的存到一起。查找的时候呢,循着这个特征找,就可以很快地找到这个数了。这个特征也要定好,使得随机的数据里面用有着相同特征的个数尽量少。

 

2016-02-04 09:14:28

 

-----------------------------------------

2016-08-27 11:29:01 更新

posted @ 2016-02-03 20:12  konjak魔芋  阅读(790)  评论(0编辑  收藏  举报