Description
- 给定 , 求 内满足 的 的个数。
- 。
- , 均为质数且互不相同, 。
- 每个测试点数据组数 。
- 时间限制 ,空间限制 。
Solution
- 非常有意思的一道题。
- 感觉让我这个初学数论的菜鸡学到了很多东西。
算法一
- 原方程等价于
- 的取值范围可以对应到 ,这样我们只需要判断模 意义下的解的个数即可。
- 这个同余方程成立,等价于对于每个
- 由于 是质数,我们可以知道上面这个方程又等价于 或
- 上面两个方程解出来的 均是 的剩余类。
- 那么我们就取模 的值作为方程的解。
- 显然两个方程解出的 不会有同一个。
- 所以方程 的解数就是上面两个方程的解数之和。
- 我们假设 表示我们对方程 解出的所有 模 的值组成的集合。
- 那么,因为 互不相同,根据中国剩余定理,我们对于每个同余方程组:
- 我们都能得到模 意义下的唯一解。
- 所以解数就是所有集合 的大小的乘积。
- 我们回到刚刚的两个方程: 或
- 对于方程1,我们只需要取 一个解。
- 对于方程2,暴力枚举验证的每次时间复杂度是 的。
- 但是时间复杂度为 ,并不能通过本题。
算法二
- 现在的问题就是如何快速求出形如
- 的方程在模 意义下的解的数量。
- 我们定义数论函数 。
- 显然 是完全积性函数。
- 我们可以使用线性筛线性筛出,对于每个质数暴力快速幂计算。
- 内质数个数是 的,然后对于每个质数 计算,总的时间复杂度可以认为是 ,然后线性筛部分是 的。
- 因此我们可以每次 处理出来这个函数在 上的所有值对 取模的值,并 求出解的数量,实现 的时间复杂度。
#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;
}
算法三
- 实际上这道题有很优秀的做法。
- 此算法来自某个神仙考场上猜的结论。
- 方程 在模 意义下的解的数量为 。
- 我们来证明一下这个显而易见的结论。
- 证明:
- 的取值范围为 。
- 这个方程形如一个 次剩余的形式。
- 我们令 表示 的一个原根。
- 根据原根的性质 ,集合 和集合 相等。
- 设 ( )。
- 所以原方程可以表示为 。
- 根据 ,我们得到 。
- 写成同余方程的形式即 。
- 显然我们可以取特解 ,得到 的通解为 。
- 我们代到 的取值范围中即有 。
- 解出来 可以取 内的任意整数。
- 证毕。
- 那么对于每个质数就可以 的时间复杂度求出 算答案了。
- 答案即为 。
- 可以轻松通过本题。
#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;
}