Loading

【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;
}
posted @ 2019-08-04 21:38  gz-gary  阅读(163)  评论(0编辑  收藏  举报