Description
- 给定 n,mn,mn,m, 求 [1,n][1,n][1,n] 内满足 n∣xm−xn|x^m-xn∣xm−x 的 xxx 的个数。
- m≤109m\le 10^9m≤109。
- n=∏i=1cpin=\prod_{i=1}^c p_in=∏i=1cpi,pip_ipi 均为质数且互不相同, pi≤10000,c≤50p_i\le 10000,c\le50pi≤10000,c≤50。
- 每个测试点数据组数 ≤10000\le 10000≤10000。
- 时间限制 5s5s5s,空间限制 256MB256MB256MB。
Solution
- 非常有意思的一道题。
- 感觉让我这个初学数论的菜鸡学到了很多东西。
算法一
- 原方程等价于 x(xm−1−1)≡0(mod n)x(x^{m-1}-1)\equiv 0(mod~n)x(xm−1−1)≡0(mod n)
- xxx 的取值范围可以对应到 [0,n)[0,n)[0,n),这样我们只需要判断模 nnn 意义下的解的个数即可。
- 这个同余方程成立,等价于对于每个 iii x(xm−1−1)≡0(mod pi)x(x^{m-1}-1)\equiv 0(mod~p_i)x(xm−1−1)≡0(mod pi)
- 由于 pip_ipi 是质数,我们可以知道上面这个方程又等价于 x≡0(mod pi)x\equiv 0(mod~p_i)x≡0(mod pi) 或 xm−1≡1(mod pi)x^{m-1}\equiv 1(mod~p_i)xm−1≡1(mod pi)
- 上面两个方程解出来的 xxx 均是 pip_ipi 的剩余类。
- 那么我们就取模 ppp 的值作为方程的解。
- 显然两个方程解出的 xxx 不会有同一个。
- 所以方程 x(xm−1−1)≡0(mod pi)x(x^{m-1}-1)\equiv 0(mod~p_i)x(xm−1−1)≡0(mod pi) 的解数就是上面两个方程的解数之和。
- 我们假设 AiA_iAi 表示我们对方程 x(xm−1−1)≡0(mod pi)x(x^{m-1}-1)\equiv 0(mod~p_i)x(xm−1−1)≡0(mod pi) 解出的所有 xxx 模 pip_ipi 的值组成的集合。
- 那么,因为 pip_ipi 互不相同,根据中国剩余定理,我们对于每个同余方程组:
∀i∈[1,c],x≡ai(mod pi),ai∈Ai \forall i\in[1,c],x\equiv a_i(mod~p_i),a_i\in A_i ∀i∈[1,c],x≡ai(mod pi),ai∈Ai
- 我们都能得到模 nnn 意义下的唯一解。
- 所以解数就是所有集合 AiA_iAi 的大小的乘积。
- 我们回到刚刚的两个方程:x≡0(mod pi)x\equiv 0(mod~p_i)x≡0(mod pi) 或 xm−1≡1(mod pi)x^{m-1}\equiv 1(mod~p_i)xm−1≡1(mod pi)
- 对于方程1,我们只需要取 x=0x=0x=0 一个解。
- 对于方程2,暴力枚举验证的每次时间复杂度是 O(pilogm)O(p_i\log m)O(pilogm) 的。
- 但是时间复杂度为 O(T∑i=1cpilogm)O(T\sum_{i=1}^c p_i\log m)O(T∑i=1cpilogm),并不能通过本题。
算法二
- 现在的问题就是如何快速求出形如xn≡1(mod p),p是质数x^n \equiv 1(mod~p),p是质数xn≡1(mod p),p是质数
- 的方程在模 ppp 意义下的解的数量。
- 我们定义数论函数 f(x)=xnf(x)=x^nf(x)=xn。
- 显然 f(x)f(x)f(x) 是完全积性函数。
- 我们可以使用线性筛线性筛出,对于每个质数暴力快速幂计算。
- [1,p)[1,p)[1,p) 内质数个数是 O(plnp)O(\frac{p}{\ln p})O(lnpp) 的,然后对于每个质数 O(logn)O(\log n)O(logn) 计算,总的时间复杂度可以认为是 O(p)O(p)O(p),然后线性筛部分是 O(p)O(p)O(p) 的。
- 因此我们可以每次 O(p)O(p)O(p) 处理出来这个函数在 [0,p)[0,p)[0,p) 上的所有值对 ppp 取模的值,并 O(p)O(p)O(p) 求出解的数量,实现 O(T∑i=1cpi)O(T\sum_{i=1}^c p_i)O(T∑i=1cpi) 的时间复杂度。
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
template <class T>
inline void read(T &x)
{
static char ch;
while (!isdigit(ch = getchar()));
x = ch - '0';
while (isdigit(ch = getchar()))
x = x * 10 + ch - '0';
}
const int MaxN = 1e4 + 5;
int id, T, c, m, n;
inline int qpow(int a, int b, const int &mod)
{
int res = 1;
for (; b; b >>= 1, a = 1LL * a * a % mod)
if (b & 1)
res = 1LL * res * a % mod;
return res;
}
//the number of x (x^m=p)(x in [0,p))
inline int solve(const int &m, const int &p)
{
static bool sie[MaxN];
static int pri[MaxN], f[MaxN], cnt;
int res = 1;
memset(sie, 0, sizeof(sie));
cnt = 0;
f[1] = 1;
for (int i = 2; i < p; ++i)
{
if (!sie[i])
{
pri[++cnt] = i;
f[i] = qpow(i, m, p);
}
for (int j = 1; j <= m; ++j)
{
int x = i * pri[j];
if (x >= p) break;
sie[x] = true;
f[x] = 1LL * f[i] * f[pri[j]] % p;
if (i % pri[j] == 0)
break;
}
if (f[i] == 1)
++res;
}
return res;
}
int main()
{
freopen("division.in", "r", stdin);
freopen("division.out", "w", stdout);
#define mod 998244353
read(id), read(T);
while (T--)
{
read(c), read(m);
int ans = 1;
int x;
for (int i = 1; i <= c; ++i)
{
read(x);
ans = 1LL * ans * (solve(m - 1, x) + 1) % mod;
}
printf("%d\n", ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
算法三
- 实际上这道题有很优秀的做法。
- 此算法来自某个神仙考场上猜的结论。
- 方程 xn≡1(mod p),p是质数x^n \equiv 1(mod~p),p是质数xn≡1(mod p),p是质数 在模 ppp 意义下的解的数量为 gcd(n,p−1)gcd(n,p-1)gcd(n,p−1)。
- 我们来证明一下这个显而易见的结论。
- 证明:
- xxx 的取值范围为 [1,p)[1,p)[1,p)。
- 这个方程形如一个 NNN 次剩余的形式。
- 我们令 ggg 表示 ppp 的一个原根。
- 根据原根的性质 ,集合 {x∣x=gk mod p,k∈[1,p),k∈Z}\{x|x=g^k\bmod p,k\in [1,p),k\in \mathbb Z\}{x∣x=gkmodp,k∈[1,p),k∈Z} 和集合 {1,2,…,p−1}\{1,2,\dots,p-1\}{1,2,…,p−1} 相等。
- 设 x=gyx=g^yx=gy(y∈[1,p)y\in[1,p)y∈[1,p))。
- 所以原方程可以表示为 gn×y≡g0(mod p)g^{n\times y}\equiv g^0(mod~p)gn×y≡g0(mod p)。
- 根据 gp−1=1g^{p-1}=1gp−1=1,我们得到 n×y≡0(mod (p−1))n\times y\equiv 0(mod~(p-1))n×y≡0(mod (p−1))。
- 写成同余方程的形式即 (p−1)x+ny=0(p-1)x+ny=0(p−1)x+ny=0。
- 显然我们可以取特解 x=y=0x=y=0x=y=0,得到 yyy 的通解为 k⋅p−1(p−1,n)(k∈Z)k·\frac{p-1}{(p-1,n)}(k\in \mathbb Z)k⋅(p−1,n)p−1(k∈Z)。
- 我们代到 yyy 的取值范围中即有 1≤k⋅p−1(p−1,n)≤p−11\le k·\frac{p-1}{(p-1,n)}\le p-11≤k⋅(p−1,n)p−1≤p−1。
- 解出来 kkk 可以取 [1,(p−1,n)][1,(p-1,n)][1,(p−1,n)] 内的任意整数。
- 证毕。
- 那么对于每个质数就可以 O(log p)O(log~p)O(log p) 的时间复杂度求出 gcdgcdgcd 算答案了。
- 答案即为 ∏i=1c(gcd(m−1,p−1)+1)\prod_{i=1}^c(gcd(m-1,p-1)+1)∏i=1c(gcd(m−1,p−1)+1)。
- 可以轻松通过本题。
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const int mod = 998244353;
int id, T, c, m, n;
int main()
{
freopen("division.in", "r", stdin);
freopen("division.out", "w", stdout);
scanf("%d%d", &id, &T);
while (T--)
{
scanf("%d%d", &c, &m);
int ans = 1;
int x;
for (int i = 1; i <= c; ++i)
{
scanf("%d", &x);
ans = 1LL * ans * (1 + std::__gcd(m - 1, x - 1)) % mod;
}
std::cout << ans << std::endl;
}
fclose(stdin);
fclose(stdout);
return 0;
}