【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, mleq 10^9)

解析

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

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

({x_i}^m-x_i equiv 0 (mod p_i) 1leq 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
", ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zjlcnblogs/p/11299900.html