【poj3243】拓展BSGS(附hash版)

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

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

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

 

 

          【poj3243】拓展BSGS(附hash版)_第1张图片

 

 

代码如下:

 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;
13 
14 struct node
15 {
16     int idx;LL val;
17 }baby[Maxn];
18 
19 LL ax,ay;
20 LL exgcd(LL a,LL b)
21 {
22     if(b==0) {ax=1,ay=0;return a;}
23     LL g=exgcd(b,a%b);
24     LL yy=ay;
25     ay=ax-a/b*ay;ax=yy;
26     return g;
27 }
28 
29 bool cmp(node x,node y) {return x.val==y.val?x.idx<y.idx:x.val<y.val;}
30 
31 int ffind(LL x)
32 {
33     int head=0,tail=cnt;
34     while(head<tail)
35     {
36         int mid=(head+tail)>>1;
37         if(baby[mid].val==x) return baby[mid].idx;
38         if(baby[mid].val>x) tail=mid-1;
39         else head=mid+1;
40     }
41     if(baby[head].val==x) return baby[head].idx;
42     return -1;
43 }
44 
45 int main()
46 {
47     while(1)
48     {
49         scanf("%lld%lld%lld",&x,&z,&k);
50         if(x==0&&z==0&&k==0) break;
51         k%=z;
52         //if(z!=1&&k==1) {printf("0\n");continue;}
53         aa=1;
54         LL g,num=0,ans=-1,bm;
55         bm=1%z;
56         bool ok=1;
57         for(int i=0;i<=100;i++) if(bm==k) {printf("%d\n",i);ok=0;break;}
58         else bm=(bm*x)%z;
59         if(ok==0) continue;
60         ok=1;
61         while((g=exgcd(x,z))!=1)
62         {
63             aa=(aa*x/g)%z,z/=g;num++;
64             if(k%g!=0) {ok=0;break;}
65             k/=g;
66         }
67         if(ok)
68         {
69             baby[0].idx=0,baby[0].val=aa%z;
70             m=(LL)(ceil(double(sqrt((double)z))));
71             for(int i=1;i<=m;i++) baby[i].idx=i,baby[i].val=(baby[i-1].val*x)%z;
72             bm=1%z;
73             for(int i=1;i<=m;i++) bm=(bm*x)%z;
74             g=exgcd(bm,z);
75             bm=ax/g;
76             bm=(bm%(z/g)+(z/g))%(z/g);
77             if(bm==0) bm=z/g;
78             sort(baby,baby+m+1,cmp);
79             cnt=0;
80             for(int i=1;i<=m;i++) if(baby[i].val!=baby[cnt].val) baby[++cnt]=baby[i];
81             LL tmp=k;
82             for(int i=0;i<=m;i++)
83             {
84                 int j;
85                 if((j=ffind(tmp))!=-1)
86                 {
87                     ans=i*m+j;
88                     break;
89                 }
90                 tmp=(tmp*bm)%z;
91             }
92         }
93         if(aa==1&&z!=1&&k==1) printf("0\n");
94         else if(ok==0||ans==-1) printf("No Solution\n");
95         else printf("%lld\n",ans+num);
96     }
97     return 0;
98 }
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 
 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(hash

 

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

 

2016-02-04 09:14:28

你可能感兴趣的:(【poj3243】拓展BSGS(附hash版))