1. 程式人生 > >HDU4992 求所有原根

HDU4992 求所有原根

margin ++i 質數 for each n) while 正整數 esp ini

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)>>1
using 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 求所有原根