洛谷P6091 【模板】原根

题目大意

给定整数 \(n\),求它的所有原根。为了减小你的输出量,给出输出参数 \(d\),设 \(n\) 的所有原根有 \(c\) 个,从小到大分别为 \(g_1,g_2,\cdots,g_c\),你只需要依次输出 \(g_d,g_{2d},\cdots,g_{\lfloor\frac{c}{d}\rfloor\times d}\)\((n\leq 10^6)\)

对于一个正整数 \(a\),若\(\gcd(a,m)=1\),则满足 \(a^x\equiv1\pmod m\) 的最小的正整数 \(x\) 称为 \(a\) 在模 \(m\) 意义下的阶,记作 \(\mathrm{ord}_m(a)\)

\(a^n\equiv 1\pmod m\) 的充要条件是 \(\mathrm{ord}_m(a)|n\)。由欧拉定理,当 \(gcd(a,m)=1\) 时,\(a^{\varphi(m)}\equiv 1\pmod m\),所以 \(\mathrm{ord}_m(a)|\varphi(m)\)

原根

\(\mathrm{ord}_p(g)=\varphi(p)\),则称 \(g\) 是模 \(p\) 意义下的原根。

只有形如 \(1,2,4,p^x,2p^x\) 的数存在原根,其中 \(p\) 是奇质数,\(x\) 是正整数。

如果 \(p\) 存在原根,则模 \(p\) 意义下的原根数量为 \(\varphi(\varphi(p))\)

如果 \(g\) 是模 \(p\) 意义下的原根,若 \(\gcd(x,\varphi(p))=1\),则 \(g^x\) 也是模 \(p\) 意义下的原根。

\(g\) 是模 \(m\) 意义下的原根,则对于所有的 \(\varphi(m)\) 的质因子 \(p\),都有 \(g^{\frac{\varphi(m)}{p}}\not\equiv 1\pmod m\)

\(10^9\) 范围内,最小的原根大小大约不超过 \(n^{0.25}\)

首先线性筛出质数和欧拉函数,把所有存在原根的数存入一个vector并排序。对于 \(p\),二分查找它是否在vector中出现。若 \(p\) 存在原根,我们从小到大开始枚举 \(g\)。设 \(\varphi(p)=\prod p_i^{k_i}\)\(p_i\) 是质数,若对于所有的 \(p_i\)\(g^{\frac{\varphi(p)}{p_i}}\not\equiv 1\pmod p\),则 \(g\) 是最小的原根,直接返回。再枚举所有小于等于 \(p\)\(\gcd(x,\varphi(p))=1\) 的正整数 \(x\)\(g^x\)均为原根。

Code

#include <bits/stdc++.h>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType& T) {
    elemType X = 0, w = 0; char ch = 0;
    while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
    T = (w ? -X : X);
}

const int maxn = 1000005;
vector<int> Prime, pk, ans;
int phi[maxn], factor[105];
bool not_Prime[maxn];
int factor_cnt;

LL GCD(LL a, LL b) { return b == 0 ? a : GCD(b, a % b); }

inline void Get_Phi(int Len) {
    not_Prime[1] = true;
    phi[1] = 1;
    int Count = 0;
    for (int i = 2; i <= Len; i++) {
        if (!not_Prime[i]) {
            Prime.push_back(i);
            ++Count; phi[i] = i - 1;
        }
        for (int j = 0; j < Count; j++) {
            int mid = i * Prime[j];
            if (mid > Len) break;
            not_Prime[mid] = true;
            if (i % Prime[j] == 0) {
                phi[mid] = phi[i] * Prime[j];
                break;
            }
            phi[mid] = phi[i] * (Prime[j] - 1);
        }
    }
    return;
}

LL ExPow(LL b, LL n, LL MOD) {
    if (MOD == 1) return 0;
    LL x = 1;
    LL Power = b % MOD;
    while (n) {
        if (n & 1) x = x * Power % MOD;
        Power = Power * Power % MOD;
        n >>= 1;
    }
    return x;
}

bool judge_rt(LL g, LL p) {
    for (int i = 1; i <= factor_cnt; ++i)
        if (ExPow(g, phi[p] / factor[i], p) == 1) return false;
    return true;
}

int get_rt(int p) {
    if (p == 1 || p == 2) return 1;
    if (p == 4) return 3;
    if (p % 4 == 0) return -1;
    if (p % 2 == 0 && !binary_search(pk.begin(), pk.end(), p >> 1)) return -1;
    if (p % 2 != 0 && !binary_search(pk.begin(), pk.end(), p)) return -1;

    for (int g = 1;; ++g) {
        if (GCD(g, p) != 1) continue;
        int cnt = 0, x = phi[p];
        factor_cnt = 0;
        for (int i = 0; (LL)Prime[i] * (LL)Prime[i] <= x; ++i) {
            if (x % Prime[i] == 0) {
                factor[++factor_cnt] = Prime[i];
                while (x % Prime[i] == 0) x /= Prime[i];
            }
        }
        if (x != 1) factor[++factor_cnt] = x;
        if (judge_rt(g, p)) return g;
    }
    return -1;
}

void init(int len) {
    Get_Phi(len);
    for (int i = 1; i < Prime.size(); ++i) {
        LL temp = Prime[i];
        while (temp <= len) { pk.push_back(temp); temp *= Prime[i]; }
    }
    sort(pk.begin(), pk.end());
}

int T, p, d;

int main() {
    init(1000000);
    Read(T);
    while (T--) {
        Read(p); Read(d);
        int g = get_rt(p);
        if (g == -1) { printf("0\n\n"); continue; }
        printf("%d\n", phi[phi[p]]);
        ans.clear();
        LL prod = 1;
        for (int i = 1; i <= phi[p]; ++i) {
            prod = prod * g % p;
            if (GCD(i, phi[p]) == 1) ans.push_back(prod);
        }
        sort(ans.begin(), ans.end());
        for (int i = 1; i * d <= ans.size(); ++i)
            printf("%d ", ans[i * d - 1]);
        printf("\n");
    }

    return 0;
}
posted @ 2021-02-03 23:47  AE酱  阅读(135)  评论(0编辑  收藏  举报