首页 > 代码库 > HDU4992 求所有原根

HDU4992 求所有原根

Primitive Roots

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 583    Accepted Submission(s): 144

Problem Description
We say that integer x, 0 < x < n, is a primitive root modulo n if and only if the minimum positive integer y which makes xy = 1 (mod n) true is φ(n) .Here φ(n) is an arithmetic function that counts the totatives of n, that is, the positive integers less than or equal to n that are relatively prime to n. Write a program which given any positive integer n( 2 <= n < 1000000) outputs all primitive roots of n in ascending order.
 
Input
Multi test cases.
Each line of the input contains a positive integer n. Input is terminated by the end-of-file seperator.
Output
For each n, outputs all primitive roots of n in ascending order in a single line, if there is no primitive root for n just print -1 in a single line.
 
Sample Input
4 25
Sample Output
3 2 3 8 12 13 17 22 23
 
这个题大概就是要你把一个数的所有比它小的原根求出来。
所谓原根就是说,对于一个数n,xk≡1(mod n)的最小正整数k是φ(n),那么就称x是n的原根。
题目里面也讲了。φ(n)就是欧拉函数。
原根有很多美丽的性质。比如说:
  1. 有原根的数只有2,4,p^n,2p^n(p为质数,n为正整数)。
  2. 一个数的最小原根的大小是O(n0.25)的。
  3. 如果g为n的原根,则gd为m的原根的充要条件是(d,φ(n))=1;
  4. 如果n有原根,它的原根个数为φ(φ(n))。

那么来看一下这道题:

首先根据性质1,我们可以通过预处理质数,把不存在的情况判掉。

然后根据性质3,找到一个原根后枚举次方判gcd就可以了。

怎么找到一个原根呢?按照性质2傻傻去跑在这种多组数据的题目里是肯定不行的。

那么有一个喜大普奔的结论来帮助我们:

因为gφ(n)≡1(mod n),而对于比φ(n)小的数都不成立。

枚举φ(n)的质因子p,看gφ(n)/p在模意义下是否是1。

是1的话g就不是原根。

证明起来有点麻烦,这里就不写了。

所以找原根大概是O(n0.25/2)的。

找到之后枚举次方就可以了,因为是充分条件。

想剪个好枝却剪烂的某人在HDU上留下了5个WA... ...

#include    <iostream>#include    <cstdio>#include    <cstdlib>#include    <algorithm>#include    <vector>#include    <cstring>#include    <queue>#define LL long long int#define ls (x << 1)#define rs (x << 1 | 1)#define MID int mid=(l+r)>>1using namespace std;const int N = 1000000+10;int P[N],vis[N],phi[N],tot,n;inline int gcd(int a,int b){return b?gcd(b,a%b):a;}inline void prepare(){  phi[1]=1;  for(int i=2;i<N;++i){    if(!vis[i])P[++tot]=i,phi[i]=i-1;    for(int j=1;j<=tot;++j){      if(i*P[j]>=N)break;      vis[i*P[j]]=1;      if(i%P[j])phi[i*P[j]]=phi[i]*phi[P[j]];      else{phi[i*P[j]]=phi[i]*P[j];break;}    }  }}inline int QPow(int d,int z,int Mod){  int ans=1;  for(;z;z>>=1,d=1ll*d*d%Mod)if(z&1)ans=1ll*ans*d%Mod;  return ans;}inline bool check(int x){  if(x==2 || x==4)return 1;  if((x&1)^1)x>>=1;  for(int i=2;P[i]<=x;++i)    if(x%P[i]==0){      while(x%P[i]==0)x/=P[i];      return x==1?P[i]:0;    }  return 0;}inline int get_rg(int fx){  int pt[1010],tt=0,Txd=phi[fx];  for(int i=1;P[i]*P[i]<=Txd;++i)    if(Txd%P[i]==0){      pt[++tt]=P[i];      while(Txd%P[i]==0)Txd/=P[i];    }  if(Txd!=1)pt[++tt]=Txd;  for(int i=2;i<=fx;++i)    if(QPow(i,phi[fx],fx)==1){      int flag=1;      for(int j=1;j<=tt;++j)	if(QPow(i,phi[fx]/pt[j],fx)==1){	  flag=0;break;	}      if(flag)return i;    }  return 0;}inline void work(int fx){  int tt=0,pr[N];  if(fx==2){printf("1\n");return;}  if(fx==4){printf("3\n");return;}  int T=check(fx);  if(!T){printf("-1\n");return;}  int g=get_rg(fx);  for(int i=1,k=g;i<phi[fx];++i,k=1ll*k*g%fx)    if(gcd(i,phi[fx])==1)      pr[++tt]=k;  sort(pr+1,pr+tt+1);  for(int i=1;i<tt;++i)    printf("%d ",pr[i]);  printf("%d",pr[tt]);  printf("\n");}int main(){  prepare();  while(scanf("%d",&n)!=EOF)work(n);  return 0;}

  

HDU4992 求所有原根