代码改变世界

SPOJ Primitive Root

2013-05-17 21:26  bootstar  阅读(158)  评论(0编辑  收藏  举报

SPOJ Primitive Root

给定一个素数P,给定n个数,判断这个n个数是不是P的原根。由原根的定义知: a^x = 1(mod P), x = phi[P],其中x是最小正解。所以也就是求解方程a^x = 1(mod P)的最小正解x满足方程。那么很直接的办法就是用Baby-Step-Giant-Step求解,但是复杂度很高。由欧拉定理知道:a^(Phi[P]) = 1(mod P),那么如果a不是P的原根,也就是说x<Phi[p],很容易证明Phi[p]%x==0。那么我们可以这么做,枚举Phi[p]的每一个约数i(i != Phi[p]), 判断(a^i)%P==1是否成立。如果成立,那么说明a不是P的原根。这样复杂度就降低为O(log(T)),其中T是P的所有约数的乘积,T不会超过2^100。

 

#include <stdio.h>
#include <string.h>
typedef long long LL;
const int maxn = 50000;
int prim[maxn+10], f[100], amount[100], cnt;

void init(){
    for(int i = 2; i <= maxn; i ++){
        if(!prim[i]) prim[cnt ++] = i;
        for(int j = i+i; j <= maxn; j += i){
            prim[j] = 1;
        }
    }
}
int calcFactor(LL n){
    int l = 0;
    for(int i = 0; i < cnt; i ++){
        if(n%prim[i]==0){
            f[l] = prim[i];
            int t = 0;
            while(n%prim[i]==0) n = n/prim[i], t ++;
            amount[l ++] = t;
        }
    }
    if(n!=1) f[l] = n, amount[l++] = 1;
    return l;
}

LL pow_mod(LL a, LL n, LL mod){
    LL ret = 1;
    while(n){
        if(n&1) ret = ret * a%mod;
        a = a * a%mod;
        n>>=1;
    }
    return ret;
}
bool dfs(LL a, LL phi, LL mod, LL v, int l, int cur){
    if(cur==l) return false;
    LL k = 1;
    for(int i = 0; i <= amount[cur]; i ++){
        if(i) k = k * f[cur];
        if(v*k!=1 && v*k!=phi && (pow_mod(a, v*k, mod) ==1 || pow_mod(a, phi/v/k, mod)==1))
            return true;
        bool ok = dfs(a, phi, mod, v*k, l, cur+1);
        if(ok) return true;
    }
    return false;
}

int main(){
    LL p, a;
    int n;
    init();
    while(scanf("%lld%d", &p, &n), n&&p){
        LL phi = p-1;
        int l = calcFactor(phi);
        while(n--){
            scanf("%lld", &a);
            bool ok = dfs(a, phi, p, 1, l, 0);
            puts(ok ? "NO" : "YES");
        }
    }
    return 0;
}