SPOJ Primitive Root
2013-05-17 21:26 bootstar 阅读(167) 评论(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;
}
浙公网安备 33010602011771号