SPOJ Primitive Root
2013-05-17 21:26 bootstar 阅读(158) 评论(0) 编辑 收藏 举报给定一个素数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; }