版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原根初学
(前言)
听说学FFT有用?先理解一下再说
(一)阶的定义
- 设
,且
,则使得
的最小的正整数
称作是 对模 的阶,记作
(二)原根的定义
- 如果
,则称
为
的一个原根
常见的,2的原根是1,4的原根是3
(三)原根的性质
- 如果一个数p有原根的话,则它共有 个原根
- 一个数p有原根的充分必要条件是 ,其中, 为非2的素数, 为正整数
(四)最小原根的求法
- 将欧拉函数值唯一分解,
若满足 ,其中
因为原根一般都比较小,所以直接遍历即可
我一开始在想,为什么4的原根为什么不是2,因为如果按照这个公式的话,不是应该只有一个质因子2嘛?
,符合公式,那么直接输出2了呗?但是我忽略了一个前提,就是阶的定义,
必须是1!!!!所以2显然不是4的原根,这时候按照公式算出来就是错的
(五)所有原根的求法
找到最小原根a之后,所有与 , 都是 的原根
证明就先略了……
例题1:51nod 1135 求单个数字的最小原根
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (ll x=a;x<=b;x++)
#define repp(x,a,b) for (ll x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const ll maxn=7e4+7;
const ll INF=1e9;
const ll INFF=1e18;
bool prime[maxn];
ll primenum[maxn];
ll primenum2[maxn];
ll m=0,cnt=0;
void prime_init()//质数线性筛
{
for (ll i=2;i<=maxn;i++)prime[i]=true;
prime[0]=prime[1]=false;
for (ll i=2;i<=maxn;i++)
{
if (prime[i])primenum[m++]=i;
for (ll j=0;j<m&&primenum[j]*i<maxn;j++)
{
prime[i*primenum[j]]=false;
if (i%primenum[j]==0)break;
}
}
}
void divide(ll n)//唯一分解
{
for (ll i=0;primenum[i]<=sqrt(n+0.5);i++)
{
if (n%primenum[i]==0)
{
primenum2[cnt++]=primenum[i];
while(n%primenum[i]==0)n/=primenum[i];
}
}
if (n>1)primenum2[cnt++]=n;
}
ll qpow(ll a,ll b,ll mod)//快速幂
{
ll res=1;
while(b)
{
if (b&1)res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res%mod;
}
ll phi(ll n)//求欧拉函数
{
ll res=n;
for (ll i=2;i*i<=n;i++)
{
if (n%i==0)
{
res=res/i*(i-1);
while(n%i==0)n/=i;
}
}
if (n>1)res=res/n*(n-1);
return res;
}
int main()
{
prime_init();
scanf("%lld",&p);
ll P=phi(p);
divide(P);
for (ll i=1;;i++)
{
bool mark=true;
//对所有的质因数都进行判断
for (ll j=0;j<cnt;j++)
if (qpow(i,P/primenum2[j],p)==1)
mark=false;
if (mark){cout<<i<<endl;return 0;}
}
}
例题2:HDU 4992 求一个数所有的原根
ps:自己怎么做都过不了,一开始一直被卡内存,后来又一直TLE……
后来看了大神的题解,感觉博主整理的真好呀:
https://blog.csdn.net/Code92007/article/details/87093456
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (ll x=a;x<=b;x++)
#define repp(x,a,b) for (ll x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const ll maxn=1e6+7;
const ll INF=1e9;
const ll INFF=1e18;
bool prime[maxn];
ll phi[maxn];
ll primenum[maxn];
set<ll > S;
vector<ll > V,ans;
ll m=0,cnt=0,p;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if (b&1)res=(res*a)%mod;a=(a*a)%mod;b>>=1;}return res%mod;}
void phi_init()
//一开始傻逼了,写了个单个求值的欧拉函数和一个线性质数筛……
//实际上一个欧拉线性筛直接就能把质数给一起筛出来了……
{
phi[1]=1;
rep(i,2,maxn)prime[i]=true;
prime[0]=prime[1]=false;
repp(i,2,maxn)
{
if (prime[i])
{
primenum[m++]=i;
phi[i]=i-1;
}
for (ll j=0;j<m&&primenum[j]*i<maxn;j++)
{
prime[i*primenum[j]]=false;
if (i%primenum[j]==0)
{
phi[i*primenum[j]]=phi[i]*primenum[j];
break;
}
phi[i*primenum[j]]=phi[i]*phi[primenum[j]];
}
}
}
vector<ll> divide(ll n)
{
vector<ll > tmp;
for (ll i=0; primenum[i]<=sqrt(n+0.5); i++)
{
if (n%primenum[i]==0)
{
tmp.pb(primenum[i]);
while(n%primenum[i]==0)
n/=primenum[i];
}
}
if (n>1)
tmp.pb(n);
return tmp;
}
bool check(ll x)
{
if (x%2==0)x/=2;
if (prime[x])return true;
if (x%2==0)return false;
for (ll i=1; i<m; i++)
{
if (x%primenum[i]==0)
{
while(x%primenum[i]==0)
{
x/=primenum[i];
}
return x==1;
}
}
return false;
}
vector<ll> solve(ll p)
{
vector<ll > t;
if (p==2)
{
t.pb(1);
return t;
}
if (p==4)
{
t.pb(3);
return t;
}
if (!check(p))
{
t.pb(-1);
return t;
}
ll root,now=1;
V=divide(phi[p]);
int len=V.size();
for (ll i=2;i<p;i++)
{
if (qpow(i,phi[p],p)!=1)continue;
bool flag=true;
for (int j=0;j<len&&flag;j++)
{
if (qpow(i,phi[p]/V[j],p)==1)flag=false;
}
if (flag)
{
root=i;
break;
}
}
for (int i=1;i<phi[p];i++)
{
now=now*root%p;
if (gcd(i,phi[p])==1)t.pb(now);
}
sort(t.begin(),t.end());
//我尝试用了set和数组存,前者超时,后者超内存……可能写炸了
ll sz=unique(t.begin(),t.end())-t.begin();//去重
t.resize(sz);
return t;
}
int main()
{
phi_init();
while(~scanf("%lld",&p))
{
ans=solve(p);
int sz=ans.size();
for (int i=0;i<sz;i++)
{
printf("%lld",ans[i]);
if (i!=sz-1)printf(" ");
}
printf("\n");
}
return 0;
}