九校联考(DL24凉心模拟) 整除(中国剩余定理+原根性质)
题意简述
给定 ,求 在满足 时合法的 的数量。答案模 。单个测试点包含多组数据。
其中 由如下方式给出:
- 给定 个不超过 的质数 ,有 。
所有测试数据满足 ,数据组数 。
题解
首先,由于已经给出了 的唯一分解式 ,故我们可以将 这个式子拆成由 个形如 的同余方程组成的同余方程组。
我们先对第 个方程求出 内满足条件的数的数量,记为 。显然这些解 均不相同,故 即 在 意义下的合法余数的数量。由于所有 均为互不相同的质数,即两两互质,因此可以证明,除了 为公共解外,任意两个方程的解( 范围内)不存在交集。因此,我们在每个方程中任选一个解,构造一个新的一次同余方程组,共能得到 个不同的方程组。由于根据中国剩余定理,每个方程组在 范围内仅有唯一解,因此答案就为 。
所以,对于第 个方程,我们需要求 。
存在一种做法是使用线性筛在接近线性的时间内求所有 内的数的 次幂,然后暴力判断。不过这里,介绍一种更简单的方法。
首先给出以下结论:给定 和 ,且 为质数,那么有:
证明如下:
我们要求满足 时 的 的个数。
首先 一定满足,故不特殊考虑,最后答案 即可。接下来只考虑 的情况。由于 为质数,因此 存在一个原根 , 内的任意数在 意义下都可以表示为 的形式。这样,原方程就转化为:
根据费马小定理:
令 ,两边同时除以 ,得:
由于此时 ,因此 一定有 。由于 ,显然, 的任意小于 的非负整数倍( 倍)均满足条件,因此 共有 种合法取值。
因此满足 的 共有 个。
这样,本题的时间复杂度就从 优化至了 ( 为 的复杂度)。
代码
线性筛求幂后暴力判断代码如下(常数莫名其妙很大...):
#include<bits/stdc++.h> using namespace std; #define rg register const int e = 998244353, N = 1e4 + 10; int mod, c, m; inline void add(rg int& x, rg int y) { x += y, x -= x >= mod ? mod : 0; } inline void mul(rg int& x, rg int y) { x = 1ull * x * y % mod; } inline int qpow(rg int v, rg int p) { rg int res = 1; for (; p; p >>= 1, mul(v, v)) { if (p & 1) { mul(res, v); } } return res; } inline int solve(rg int n) { rg int p[N], f[N], pri[N], t; mod = n, t = 0; fill(p + 1, p + 1 + n, 1); rg int res = 2; for (rg int i = 2; i < n; ++i) { if (p[i]) { pri[++t] = i, f[i] = qpow(i, m); } res += (i == f[i]); for (rg int j = 1, d; j <= t && (d = i * pri[j]) <= n; ++j) { p[d] = 0; f[d] = 1ull * f[i] * f[pri[j]] % mod; if (i % pri[j] == 0) { break; } } } return res; } int main() { int T; scanf("%*d%d", &T); for (rg int kase = 1; kase <= T; ++kase) { scanf("%d%d", &c, &m); rg int ans = 1; for (rg int i = 1; i <= c; ++i) { rg int x; scanf("%d", &x); rg int v = solve(x); mod = e, mul(ans, v); } printf("%d\n", ans); } return 0; }
用更简单的方法代码如下:
#include<bits/stdc++.h> using namespace std; #define rg register const int mod = 998244353; inline void mul(int& x, int y) { x = 1ll * x * y % mod; } int main() { int T; scanf("%*d%d", &T); for (rg int kase = 1; kase <= T; ++kase) { int n, m; scanf("%d%d", &n, &m); int ans = 1; for (rg int i = 1; i <= n; ++i) { int x; scanf("%d", &x); mul(ans, __gcd(m - 1, x - 1) + 1); } printf("%d\n", ans); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?