3月5日 Primitive Roots (解原根/本原元)

解原根問題在上週一直tle沒有解決,今天折騰了半天(製造了大量access violation),坑點在於很多判斷的條件。
即使按照質因數優化的方法,如果直接遍歷2~ϕ(n) 的話,複雜度還是很高,夠tle幾十次了。原根的重要性質(模p):
gϕ(p)pi1(modp)
這個性質用來快速判斷枚舉的g是否爲原根。以下性質形成這之前的剪枝策略:

1 當整數可寫成pn 或者2pn 時,它纔有原根,其中p是素數,n是整數。
2 若g爲模p的原根,則所有原根爲gd(modp) 並且滿足(d,ϕ(p))=1 ,事實上原根個數等於ϕ(ϕ(p))

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long int
int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a%b);
}
int fast_pow(int a,int b,int p)
{
    a %= p;
    ll tmp = 1;
    while (b)
    {
        if (b & 1)tmp = (ll)tmp*a%p;
        b >>= 1;
        a = (ll)a*a%p;
    }
    return tmp;
}
#define N 1000000+10
int phi[N], pri[N];
bool notpri[N];
int cnt;
void ini()
{
    phi[1] = 1;
    for (int i = 2; i < N; i++)
    {
        if (!notpri[i])
        {
            pri[cnt++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; j < cnt; j++)
        {
            if (i*pri[j] >= N)break;
            notpri[i*pri[j]] = 1;
            if (i%pri[j] != 0)
            {
                phi[i*pri[j]] = phi[i] * (pri[j] - 1);
            }
            else
            {
                phi[i*pri[j]] = phi[i] * pri[j];
                break;
            }
        }
    }
}
bool check(int a)//剪枝
{
    if (a == 2 || a == 4)return 1;
    if ((a & 1) ^ 1)a >>= 1;//這行是學習的其他博客的,最後一位是0
    //偶數一定不是【除2以外】質數的冪
    for (int i = 1; i < cnt; i++)
    {
        int p = pri[i];
        if (p > a)break;
        if (a%p == 0)
        {
            while (a%p == 0)a /= p;
            if (a == 1)return 1;
            else return 0;
        }
    }
    return 0;
}
int np;
int pf[1005];
int rt(int a)//解原根
{
    //if (!check(a))return -1;
    memset(pf, 0, sizeof(pf));
    np = 0;//初始化
    int cpphi=phi[a];
    int k = ceil(sqrt(phi[a]*1.0));
    for (int i = 0; i < cnt; i++)//分解質因數
    {
        if (pri[i] > k)
        {
            if (cpphi != 1)pf[np++] = cpphi;
            break;
        }
        if (cpphi%pri[i] == 0)
        {
            pf[np++] = pri[i];
            while (cpphi%pri[i] == 0)cpphi /= pri[i];
            if (cpphi == 1)break;
        }
    }
    for (int i = 2; i < a; i++)
    {
        if (fast_pow(i, phi[a], a) == 1)
        {
            bool flg = 1;
            for (int j = 0; j < np; j++)
            {
                if (fast_pow(i, phi[a] / pf[j], a) == 1)
                {
                    flg = 0;
                    break;
                }
            }
            if(flg)return i;
        }
    }
    return -1;
}
void solve(int a)
{
    if (a == 2)
    {
        printf("1\n");
        return;
    }
    bool T = check(a);
    if (!T) { printf("-1\n"); return; }
    int pr[N];
    int nump = 0;
    int g = rt(a);
    if(g==-1)
    {
        printf("-1\n");
        return;
    }
    ll gp = g;
    for (int p = 1; p < phi[a]; gp = gp*g%a, p++)
    {
        if (gcd(p, phi[a]) == 1)pr[nump++] = gp;
    }
    sort(pr, pr + nump);
    for (int j = 0; j < nump-1; j++)
    {
        printf("%d ", pr[j]);
    }
    printf("%d\n", pr[nump-1]);
    return;
}
int main()
{
    int a;
    ini();
    while (scanf("%d", &a) != EOF)
    {
        solve(a);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章