1. 程式人生 > >51Nod 1135-原根(快速求解一個素數的原根)

51Nod 1135-原根(快速求解一個素數的原根)

題目地址:51Nod 1135
1.原根定義:設m>1,gcd(a,m)=1,使得這裡寫圖片描述成立的最小的r,稱為a對模m的階。
2.定理:如果模m有原根,那麼他一共有這裡寫圖片描述個原根。
3.定理:如果p為素數,那麼素數p一定存在原根,並且模p的原根的個數為這裡寫圖片描述個。
4.定理:假設m是正整數,a是整數,如果a模m的階等於這裡寫圖片描述,則稱a為模m的一個原根。
5.模m有原根的充要條件:m=2,4,P^a,2*P^a…….
求模素數P的原根的方法:對P-1素因子分解,即P-1=(P1^a1)(P2^a2)…..(Pk^ak)。,若恆有這裡寫圖片描述成立,那麼g就是P的原根(對於合數而言,只需要把p-1換成這裡寫圖片描述即可)

#include <stdio.h>
#include <math.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <sstream> #include <algorithm> #include <set> #include <queue> #include <stack> #include <map> #include <bitset> #pragma comment(linker, "/STACK:102400000,102400000")
using namespace std; typedef long long LL; const int inf=0x3f3f3f3f; const double pi= acos(-1.0); const double esp=1e-7; const int Maxn=1e6+10; int prime[Maxn];//儲存素數 int sprime[Maxn];//儲存P-1的素因子 bitset<Maxn>pri;//結果只有0和1,判斷是否為素數 int k;//記錄Maxn以內的素數個數 int cnt;//記錄素因子的個數 void is_prime() { pri.set();//將所有的二進位制數都標為1
for(int i=2; i<Maxn; i++) { if(pri[i]) { prime[k++]=i; for(int j=i+i; j<Maxn; j+=i) pri[j]=0; } } } void Divide(int n)//將n分解為素因子 { cnt=0; int t=(int)sqrt(1.0*n); for(int i=0; prime[i]<=t; i++) { if(n%prime[i]==0) { sprime[cnt++]=prime[i]; while(n%prime[i]==0)//因為有可能有多個peime[i] n/=prime[i]; } } if(n>1) sprime[cnt++]=n;//可能只有自己一個素因子 } LL modexp(LL a,LL b,int mod)//快速冪取餘 { LL res=1; while(b>0) { a=a%mod; if(b&1) res=res*a%mod; b=b>>1; a=a*a%mod; } return res; } int main() { int p; is_prime(); while(~scanf("%d",&p)) { Divide(p-1); for(int g=2; g<p; g++) { int flag=1; for(int i=0; i<cnt; i++) { int t=(p-1)/sprime[i]; if(modexp(g,t,p)==1) { flag=0; break; } } if(flag) { int root=g; printf("%d\n",root); break;//去掉break的話是求所有的原根,加上break是求最小的原根、 } } } return 0; }