POJ 2417 Discrete Logging

解高次同余方程 BSGS算法

  具体算法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 }
AC代码(Map)

 

超时代码:

#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;
}
 
超时代码(Map)

 

posted @ 2018-03-24 13:06  Travelller  阅读(261)  评论(0编辑  收藏  举报