【JZOJ6272】【NOIP提高组A】整除 (division)

题目大意

求\([1,n]\)中满足\(n|x^m-x\)的\(x\)的个数,其中\(n\)以\(n=p_1 p_2 p_3 ... p_c\)的形式给出。
\(c \leq 50, p_i \leq 10^4, m\leq 10^9\)

解析

这题关键是\(n\)的每个质因子都只有一个。

将方程\(x^m-x \equiv 0\ (mod\ n)\)变为下列方程:

\({x_i}^m-x_i \equiv 0\ (mod\ p_i)\ 1\leq i \leq c\)

列出同余方程组:

\(x \equiv x_i\ (mod\ p_i)\)

\(n=p_1 p_2 p_3 ... p_c\),根据中国剩余定理,在\([1,n]\)内\(x\)有且仅有一个解,即一组合法的\(x_i\)唯一对应一个\(x\)。

而将\(x\ mod\ p_i\)又能唯一对应一组\(x_i\),因此\(x\)的解数就是合法\(x_i\)的组数,即各合法\(x_i\)的解数相乘。

问题是如何快速求\({x_i}^m-x_i \equiv 0\ (mod\ p_i)\)的解数。

众所周知\(x^m\)是个积性函数,我们只需要求出\([1,p_i]\)内每个质数的\(m\)次幂,便可以用线性筛求出每个\(x_i\)的\(m\)次幂,然后暴力判断,就能AC了,但是有更快的方法。

利用了这样一个结论:\(x^m-x \equiv 0\ (mod\ p)\)的解数为\(gcd(m-1,p-1)+1\)。

证明是这样的:
\(p\)是一个质数,因此\(p\)必定有原根\(g\),根据原根的性质,\([1,p-1]\)内每个数都能用\(g^k\ mod\ p\)表示,原方程可变为:
\[g^{mk} \equiv g^k\ (mod\ p)\]
根据费马小定理:
\[mk \equiv k\ (mod\ p-1)\]
变形:
\[(m-1)k \equiv 0\ (mod\ p-1)\]
令\(y=gcd(m-1,p-1)\),得到:
\[\frac{m-1}{y}k \equiv 0\ (mod\ \frac{p-1}{y})\]
这时\(gcd(\frac{m-1}{y},\frac{p-1}{y})=1\),所以\(\frac{p-1}{y} | k\),\(k\)是在\([0,p-2]\)中的,因此\(k\)可以取到\(\frac{p-1}{y}\)的\([0,y-1]\)倍,所以合法的\(k\)有\(y\)个,对应了合法的\(x=g^k\)也有\(y\)个,但是\(x=p\)也是可以的,所以共\(y+1\)个合法的\(x\)。结论得证。

然后就能以\(O(\sum logp_i)\)的复杂度解决了,快到飞起。

Code

#include <cstdio>

typedef long long ll;
const ll P = 998244353;

ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }

int T, c;
ll p, m, ans;

int main()
{
    freopen("division.in", "r", stdin);
    freopen("division.out", "w", stdout);
    scanf("%*d%d", &T);
    while (T--)
    {
        scanf("%d%lld", &c, &m);
        ans = 1;
        for (int i = 1; i <= c; i++) scanf("%lld", &p), ans = ans * (gcd(m - 1, p - 1) + 1) % P;
        printf("%lld\n", ans);
    }
    return 0;
}
上一篇:D1. Equalizing by Division (easy version)


下一篇:2019年个人训练赛第一场-E - Equalizing by Division (hard version)