POJ 2417 Discrete Logging
具体算法BSGS↑,就说一点实际解决中的问题
1. 时间复杂度分析——选择用MAP(STL)的时候就已经是在TLE的边缘试探了
这道题做的...提交了很多遍都是超时...无奈地看了下Problem Status...发现手写的hash速度不到用map的百分之一...可以说,如果选择用map...就相当于是在超时的边缘试探了...下面两个使用map的代码唯一的区别在于第20行的判断用了stl自带的函数count...STL自带函数的速度可想而知。
另一方面理论分析一下,map内部是一个平衡二叉树,插入 log(N),查询 log(N);hash表忽略冲突的情况下,插入O(1),查询O(1),所以采用不同的数据结构还是有差距的。
2. 使用Map——处理冲突带来的问题
如果使用STL中的map,由于map中key不能重复出现,所以map不能处理冲突。但是对于题目要求的最小解 x=i*m-j ,因为是简单的线性关系,可以直接避免冲突的出现,只要使存储的 j 尽量的大,而 i 尽量的小即可。因此,遍历 j 时,对同一key取value到最大值,遍历 i 时从小开始,得到一个解就return。具体操作的时候,map不能重复插入key相同的pair(无法覆盖,相当于没有操作),也不能通过迭代器对键值value(pair的后一个元素)更改,只能用 operator [] 更改value的值(如果先find再erase妥妥超时)。但是在讲map的时候也提到,使用operator [],查询的时候,如果map中没有该键,则会自动插入相应键值为零的元素,所以两次对 i , j 的枚举会导致map中出现很多value=0的无用元素,但是通过count()判断会超时,所以只能用operator []判断。为了不将本身value=0的元素和无用元素混淆,统一在插入的时候将value加一,return x 的时候相应减一。
AC代码(手写hash):
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int mop=100007; int hash[mop]; int head[mop],nxt[mop],id[mop],cnt; void add(int x,int y){//hash int m=x%mop; hash[++cnt]=x;nxt[cnt]=head[m];id[cnt]=y;head[m]=cnt; return; } int find(int x){ int m=x%mop; int i,j; for(i=head[m];i!=-1;i=nxt[i]){ if(hash[i]==x)return id[i]; } return -1; } int BSGS(int a,int b,int n){//a^x mod n =b memset(head,-1,sizeof head); cnt=0; if(b==1)return 0; int m=sqrt((double)n),j; long long x=1,p=1; for(int i=0;i<m;i++,p=p*a%n)add(p*b%n,i);//预处理b^i for(long long i=m; ;i+=m){ x=x*p%n; if((j=find(x))!=-1)return i-j; if(i>n)break; } return -1; } int main(){ int p,b,n; while(scanf("%d%d%d",&p,&b,&n)!=EOF){ int ans=BSGS(b,n,p); if(ans==-1)printf("no solution\n");//无解 else printf("%d\n",ans); } return 0; }
AC代码(Map):
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<map> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 map<ll,int>mp; 9 10 int BSGS(int a,int b,int c){ 11 if (b==1) return 0; 12 if (a%c==0) return -1; 13 ll m=ceil(sqrt(c)); 14 long long p=1,t=1; 15 for (int i=0;i<m;i++,p=p*a%c) 16 mp[p*b%c]=i+1; 17 18 for (int i=1;i<=m;i++) { 19 t = t * p % c; 20 if (mp[t]) { 21 return i*m-mp[t]+1; 22 } 23 } 24 return -1; 25 } 26 27 int main() { 28 int a, b, c; 29 while (scanf("%d%d%d", &c, &a, &b) != EOF) { 30 mp.clear(); 31 int ans = BSGS(a,b,c); 32 if (ans == -1) printf("no solution\n"); 33 else printf("%d\n", ans); 34 } 35 return 0; 36 }
超时代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<cmath> using namespace std; typedef long long ll; map<ll,int>mp; int BSGS(int a,int b,int c){ if (b==1) return 0; if (a%c==0) return -1; ll m=ceil(sqrt(c)); long long p=1,t=1; for (int i=0;i<m;i++,p=p*a%c) mp[p*b%c]=i; for (int i=1;i<=m;i++) { t = t * p % c; if (mp.count(t)) { return i*m-mp[t]; } } return -1; } int main() { int a, b, c; while (scanf("%d%d%d", &c, &a, &b) != EOF) { mp.clear(); int ans = BSGS(a,b,c); if (ans == -1) printf("no solution\n"); else printf("%d\n", ans); } return 0; }