嘘~ 正在从服务器偷取页面 . . .

阶与原根


阶与原根

前置知识:欧拉定理、费马小定理、拉格朗日定理。

模板题P6091 【模板】原根

题意

给定整数 $n$,求它的所有原根。

为了减小你的输出量,给出输出参数 $d$,设 $n$ 的所有原根有 $c$ 个,从小到大分别为 $g_1,\ldots,g_c$,你只需要依次输出 $g_d,g_{2d},\ldots,g_{\lfloor\frac{c}{d}\rfloor\times d}$。

正整数 $g$ 是正整数 $n$ 的原根,当且仅当 $1\leq g\leq n-1$,且 $g$ 模 $n$ 的阶为 $\varphi(n)$。

输入格式

本题包含多组数据。

第一行:一个整数 $T$,表示数据组数。

接下来 $T$ 行:每行两个整数 $n,d$,表示一组询问数据。

输出格式

对于每组数据:

第一行输出一个整数 $c$,表示 $n$ 的原根个数,第二行输出 $\lfloor\dfrac{c}{d}\rfloor$ 个数,按照题目描述中要求输出。

注意:即使 $\lfloor\dfrac{c}{d}\rfloor=0$,也需要输出一个空行。

数据范围

对于 $100\%$ 的数据,$1\leq T\leq 10$,$2\leq n\leq 10^6$,$1\leq d\leq 200$,保证输出的数的总个数不超过 $10^5$。

先放模板题是因为这篇文章是模板题题解,不过我们需要先了解什么是阶,什么又是原根。


:由欧拉定理可知,对于整数 $a$ 和正整数 $m$ ,若 $\gcd(a,m)=1$ ,则

因此满足同余式 $a^n \equiv 1 \pmod{m}$ 的最小正整数 $n$ 存在,这个 $n$ 称作 $a$ 模 $m$ 的阶,记作 $\delta_m(a)$

原根:对于整数 $a$ 和正整数 $m$ ,若 $\gcd(a,m)=1$ 且 $\delta_m(a) = \varphi(m)$ ,则称 $a$ 为模 $m$ 的原根。

这里可能有一点点绕,大概就是定义了什么是阶,然后定义模 $m$ 的阶为 $\varphi(m)$ 的整数 $a$ 为「模 $m$ 的原根」。


关于阶有一个较为显然的性质:

性质1:若 $a^n \equiv 1 \pmod{m}$ ,则 $\delta_m(a) \mid n$ 。(注意这里 $n$ 和之前不一样)

证明:对 $n$ 除以 $\delta_m(a)$ 作带余除法,设

若 $r > 0$ ,则

这与 $\delta_m(a)$ 的最小性矛盾。证毕。

除此以外,阶还有两个比较重要的性质。

性质2:对于整数 $a,b$ 和正整数 $m$ ,$\gcd(a,m) = \gcd(b,m) = 1$ ,则

的充分必要条件是 $\gcd(\delta_m(a),\delta_m(b)) = 1$ 。

证明

  • 必要性证明:

    由 $a^{\delta_m(a)} \equiv 1\pmod m$ 及 $b^{\delta_m(b)} \equiv 1\pmod m$ 可知

    根据性质1,有

    又由于 $\delta_m(ab) = \delta_m(a)\delta_m(b)$ ,则

    即 $\gcd(\delta_m(a), \delta_m(b)) = 1$

  • 充分性证明:

    由 $(ab)^{\delta_m(ab)} \equiv 1 \pmod{m}$ 可知

    故 $\delta_m(a) \mid \delta_{m}(ab)\delta(b)$ 。结合 $\gcd(\delta_m(a),\delta_m(b)) = 1$ ,得

    同理可得

    所以

    另一方面,有

    综上可得

证毕。

性质3:设 $k \in \mathbb{N}$ ,对于整数 $a$ 和正整数 $m$ ,$\gcd(a,m) = 1$ ,则

证明:注意到

另一方面,由 $a^{\delta_m(a)} \equiv 1\pmod m$ 可知

综上可得

证毕。

至此我们知道了阶重要的性质,接着我们来说明怎样的 $m$ 存在原根。

首先,显然 $m=1,2,4$ 时原根存在。

定理1:对于奇素数 $p$ ,$p$ 有原根。

证明

先证明一个引理:

引理:设 $a$ 与 $b$ 是与 $p$ 互质的两个整数,则存在整数 $c$ 使得 $\delta_p(c) = \mathrm{lcm}(\delta_p(a),\delta_p(b))$

证明:记 $r = \delta_p(a),t = \delta_p(b)$ ,设他们的标准分解(这里对指数仅要求 $\max(\alpha_j,\beta_j) > 0$)分别为

令 $l$ 为所有 $\alpha_j \le \beta_j$ 的 $p_j^{\alpha_j}$ 的乘积,$m$ 为所有 $\alpha_j>\beta_j$ 的 $p_j^{\alpha_j}$ 的乘积。

记 $r = lx, t = my$ ,则这样定义的 $x,y$ 满足 $\gcd(x,y)=1$ 且 $\mathrm{lcm}(r,t) = xy$ 。

由性质3,$\delta_p(a^l) = x, \delta_p(b^m)=y$ ,由性质2,$\delta_p(a^lb^m) = xy = \mathrm{lcm}(\delta_p(a),\delta_p(b))$ ,即 $c=a^lb^m$ 。$\square$

考虑原命题,对 $1$ 到 $(p-1)$ 依次两两使用引理,可知存在整数 $g$ 使得

这表明 $\delta_p(j) \mid \delta_p(g)$ 对于 $j = 1,2,\cdots,p-1$ ,所以 $j = 1,2,\cdots,p-1$ 都是同余方程

的根。由拉格朗日定理,可知方程的次数 $\delta_p(g) \ge p - 1$

由费马小定理,$\delta_p(g) \le p-1$ ,故 $\delta_p(g) = p-1 = \varphi(p)$ 。综上可知 $g$ 为模 $p$ 的原根。$\square$

定理2:对于奇素数 $p$ ,设 $\alpha \in \mathbb{Z_+}$ ,则 $p^{\alpha}$ 有原根

证明

先证明一个引理

引理:存在模 $p$ 的原根 $g$ ,使得 $g^{p-1}\not\equiv 1\pmod{p^2}$

证明:实际上,任取模 $p$ 的原根 $g$ ,若 $g$ 不满足条件,我们认定 $g+p$ 满足条件

证毕。

考虑原命题,我们证明若 $g$ 是满足引理条件的原根,则对任意正整数 $\alpha$ ,$g$ 是模 $p^{\alpha}$ 的原根。

首先证明下面的结论:对任意正整数 $\beta$ ,都可设

当 $\beta = 1$ 时,由 $g$ 的选取可知结论成立。现设上式对 $\beta$ 时成立,则

结合 $p \not\mid k_{\beta}$ 可知命题对 $\beta+1$ 成立。所以命题对任意正整数 $\beta$ 都成立。

其次,记 $\delta = \delta_{p^{\alpha}}(g)$ ,则由欧拉定理可知 $\delta \mid p^{\alpha - 1}(p - 1)$ 。

而由 $g$ 为模 $p$ 的原根,及 $g^{\delta} \equiv 1\pmod{p^{\alpha}}$ 可知 $(p-1) \mid \delta$

所以可设 $\delta = p^{\beta - 1}(p-1)$ ,这里 $1 \le \beta \le \alpha$ 。

利用之前的结论,可知

结合 $g^{\delta}\equiv 1 \pmod{p^{\alpha}}$ 可知 $\beta \ge \alpha$ 。综上可知 $\beta = \alpha$ ,即

从而 $g$ 是模 $p$ 的原根。

定理3:对于奇素数 $p$ ,设正整数 $\alpha$ ,$2p^{\alpha}$ 的原根存在。

证明:设 $g$ 是模 $p^{\alpha}$ 的原根,则 $g + p^{\alpha}$ 也是模 $p^{\alpha}$ 的原根。

在 $g$ 和 $g+p^{\alpha}$ 中有一个奇数,设这个奇数是 $G$ ,则 $\gcd(G,2p^{\alpha}) = 1$

由欧拉定理,$\delta_{2p^{\alpha}}(G) \mid \varphi(2p^{\alpha})$ ,而 $G^{\delta_{2p^\alpha}(G)}\equiv 1 \pmod{2p^{\alpha}}$ ,故

利用 $G$ 为模 $p^{\alpha}$ 的原根可知 $G$ 为模 $2p^{\alpha}$ 的原根。证毕。

定理4:对于 $m \not\in \{1,2,4\}$ ,且不存在奇素数 $p$ 及 $\alpha \in \mathbb{Z}_+$ 使得 $m \in \{p^{\alpha},2p^{\alpha}\}$ ,

则对任意整数 $a$ 满足 $\gcd(a,m)=1$ 都有 $\delta_m(a) < \varphi(m)$ ,即模 $m$ 的原根不存在

证明:对于 $m = 2^{\alpha}$ ,其中 $\alpha \ge 3\,\land\,\alpha \in \mathbb{Z}_+$ ,则对任意奇数 $a = 2k + 1$ 均有

其中最后一步用到 $k$ 与 $(2^{\alpha - 2} - 1)\cdot k$ 同奇偶的性质,故其和为偶数。

若 $m$ 不是 $2$ 的幂,且 $m$ 为符合命题条件的数,则可设 $m = rt$ ,这里 $2 < r < t$ 且 $\gcd(r,t)=1$ 。

此时,若 $\gcd(a,m)=1$ ,由欧拉定理可知

注意到 $n > 2$ 时,$\varphi(n)$ 为偶数,所以

进而

证毕。

好好好,我们终于证明完了这些性质,总结一下对本题有用的结论是:

  • 仅有 $1,2,4$ 或奇素数 $p^{\alpha}$ 及 $2p^{\alpha}$ 有原根,其他的数都没有原根。

考虑预处理素数及其幂次,以及幂次 $\times 2$ ,接着就可以 $\mathcal{O}(1)$ 判断一个数有没有原根。

如果一个数有原根,可以先求出最小正原根,而正整数 $m$ 的最小正原根的级别不超过 $\mathcal{O}(m^{\frac{1}{4}})$

因此考虑从小到大枚举。由原根的定义,若 $g$ 为模 $m$ 的原根,则对于 $\varphi(m)$ 的任意素因数 $p$ ,必有

而满足以上条件的 $g$ 必将是原根。考虑预处理出 $\varphi(m)$ 的所有素因数,然后快速幂暴力判断

假如找到了一个原根 $g$ ,不难证明对于所有 $\gcd(x,\varphi(m)) = 1$ 的 $x$ ,$g^x$ 均为原根,

因此模 $m$ 的原根有 $\varphi(\varphi(m))$ 个,于是我们可以在找到 $g$ 后枚举找出 $m$ 的所有原根。

时间复杂度 $\mathcal{O}(n^{\frac{1}{4}}\log n)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e6 + 15))

int tot,p[N],ck[N];
int _,num,cnt,phi[N],rt[N],fc[N],ans[N];
int gcd(int a,int b) { return b == 0 ? a : gcd(b, a % b); }
int qpow(int a, int b, int p){
    int r = 1;
    while(b) { if(b & 1) r = r * a % p; b >>= 1; a = a * a % p; }
    return r;
}
void init()
{
    phi[1] = 1;
    for(int i = 2; i <= 1e6 + 5; i++)
    {
        if(!ck[i]) { p[++tot] = i; phi[i] = i - 1; }
        for(int j = 1; j <= tot && i * p[j] <= 1e6 + 5; j++)
        {
            int pos = i * p[j]; ck[pos] = 1;
            if(i % p[j]) {
                phi[pos] = phi[i] * phi[p[j]];
            }else {
                phi[pos] = phi[i] * p[j]; break;
            }
        }
    }
    rt[2] = rt[4] = 1;
    for(int i = 2; i <= tot; i++)
    {
        for(int j = 1; p[i] * j <= 1e6 + 5; j *= p[i]) { rt[p[i] * j] = 1; }
        for(int j = 2; p[i] * j <= 1e6 + 5; j *= p[i]) { rt[p[i] * j] = 1; }
    }
}
bool check(int x, int p)
{
    if(qpow(x, phi[p], p) != 1) return false;
    for(int i = 1; i <= cnt; i++)
        if(qpow(x, phi[p] / fc[i], p) == 1) return false;
    return true;
}
int findrt(int p)
{
    for(int i = 1; i < p; i++)
        if(check(i, p)) return i;
    return 0;
}
void getrt(int p, int x)
{
    int tmp = 1;
    for(int i = 1; i <= phi[p]; i++)
    {
        tmp = tmp * x % p;
        if(gcd(i, phi[p]) == 1) ans[++num] = tmp;
    }
    return;
}
void solve()
{
    int p; cin >> p >> _; num = cnt = 0;
    if(!rt[p]) return cout << "0\n\n", void(0);
    int tmp = phi[p];
    for(int i = 2; i * i <= tmp; i++)
        if(tmp % i ==0) { 
            fc[++cnt] = i;
            while(tmp % i == 0) { tmp /= i; }
        }
    if(tmp > 1) fc[++cnt] = tmp;
    int mn = findrt(p); getrt(p, mn);
    sort(ans + 1, ans + 1 + num); cout << num << '\n';
    for(int i = 1; i <= num / _; i++) cout << ans[i * _] << ' ';
    cout << '\n';
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    init(); int qwq; cin >> qwq; while(qwq--) solve();
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/ix-35/solution-p6091

[2] https://www.luogu.com.cn/blog/codecodeakioi/solution-p6091


题外话

终于把这个坑给填了,果然要花不少时间。


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录