数论阶与原根基础

原根初学

(前言)

听说学FFT有用?先理解一下再说


(一)阶的定义

  • p > 1 p>1 p>1,且 g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1,则使得
    a t ≡ 1   ( m o d   p ) a^t\equiv 1 \ (mod \ p) at1 (mod p)的最小的正整数 t t t
    称作是 a a a对模 p p p的阶,记作 δ p ( a ) \delta _p(a) δp(a)

(二)原根的定义

  • 如果 δ p ( a ) = φ ( p ) \delta_p(a)=\varphi(p) δp(a)=φ(p),则称 a a a p p p的一个原根
    常见的,2的原根是1,4的原根是3

(三)原根的性质

  • 如果一个数p有原根的话,则它共有 φ ( φ ( p ) ) \varphi(\varphi(p)) φ(φ(p))个原根
  • 一个数p有原根的充分必要条件是 p = 1 , 2 , 4 , p 1 a 1 , 2 p 1 a 1 p=1,2,4,p_1^{a_1},2p_1^{a_1} p=1,2,4,p1a1,2p1a1,其中, p 1 p_1 p1为非2的素数, a 1 a_1 a1为正整数

(四)最小原根的求法

  • 将欧拉函数值唯一分解, φ ( p ) = p 1 k 1 p 2 k 2 … … p n k n \varphi(p)=p_1^{k_1}p_2^{k_2}……p_n^{k_n} φ(p)=p1k1p2k2pnkn
    若满足 a φ ( p ) p i ≠ 1   ( m o d ≡   p ) a^{\varphi(p)\over{p_i}}\not =1 \ (mod\equiv \ p) apiφ(p)=1 (mod p)其中 1 ≤ i ≤ n 1\leq i \leq n 1in

因为原根一般都比较小,所以直接遍历即可

我一开始在想,为什么4的原根为什么不是2,因为如果按照这个公式的话,不是应该只有一个质因子2嘛? 2 2 2 % 4 ≠ 1 2^{2\over 2}\%4\not=1 222%4=1
,符合公式,那么直接输出2了呗?但是我忽略了一个前提,就是阶的定义, g c d ( a , p ) gcd(a,p) gcd(a,p)必须是1!!!!所以2显然不是4的原根,这时候按照公式算出来就是错的

(五)所有原根的求法

找到最小原根a之后,所有与 φ ( p ) 互 质 的 k \varphi(p)互质的k φ(p)k a k % p a^{k}\%p ak%p都是 p p p的原根

证明就先略了……

例题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;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值