P14928 [北大集训 2025] 深红
EuphoricStar · · 题解
尝试不用群论语言复述一下这题的做法(?,感谢 ChatGPT 老师的指导。
考虑第二问。由特殊性质启发,我们声称,答案为对所有
证明考虑设一个相似类有
考虑如何对单个平移量
考虑第一问。现在设一个相似类有
我们考虑对一个平移量的集合
考虑
设
设
所以我们进而可以证明,所有可能的
算不动集合恰好为
可以发现,设
所以我们算出
最后我们还要进行容斥,即把所有集合按照
考虑给两个三元组
考虑给定三元组
时间复杂度
:::info[代码]
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
using ll = long long;
using ull = unsigned long long;
using db = double;
using ldb = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
namespace IO {
const int maxn = 1 << 20;
char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf;
inline char gc() {
return (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++) : *iS++);
}
template<typename T = int>
inline T read() {
char c = gc();
T x = 0;
bool f = 0;
while (c < '0' || c > '9') {
f |= (c == '-');
c = gc();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = gc();
}
return f ? ~(x - 1) : x;
}
inline int reads(char *s) {
char c = gc();
int len = 0;
while (isspace(c)) {
c = gc();
}
while (!isspace(c) && c != EOF) {
s[len++] = c;
c = gc();
}
s[len] = '\0';
return len;
}
inline string reads() {
char c = gc();
string s;
while (isspace(c)) {
c = gc();
}
while (!isspace(c) && c != EOF) {
s += c;
c = gc();
}
return s;
}
inline void flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
}
struct Flusher {
~Flusher() {
flush();
}
} AutoFlush;
inline void pc(char ch) {
if (oS == obuf + maxn) {
flush();
}
*oS++ = ch;
}
inline void write(char *s) {
for (int i = 0; s[i]; ++i) {
pc(s[i]);
}
}
inline void write(const char *s) {
for (int i = 0; s[i]; ++i) {
pc(s[i]);
}
}
template<typename T>
inline void write(T x) {
static char stk[64], *tp = stk;
if (x < 0) {
x = ~(x - 1);
pc('-');
}
do {
*tp++ = x % 10;
x /= 10;
} while (x);
while (tp != stk) {
pc((*--tp) | 48);
}
}
template<typename T>
inline void writesp(T x) {
write(x);
pc(' ');
}
template<typename T>
inline void writeln(T x) {
write(x);
pc('\n');
}
}
using IO::read;
using IO::reads;
using IO::write;
using IO::pc;
using IO::writesp;
using IO::writeln;
const int maxn = 1000100;
const int mod = 998244353;
inline int qpow(int b, int p) {
int res = 1;
while (p) {
if (p & 1) {
res = 1ULL * res * b % mod;
}
b = 1ULL * b * b % mod;
p >>= 1;
}
return res;
}
inline void fix(int &x) {
x += ((x >> 31) & mod);
}
int n, m, K, a[maxn], b[maxn], c[maxn], pr[maxn / 10], mpr[maxn], tot, tt, phi[maxn], inv[maxn];
bool vis[maxn];
pii d[99];
void dfs(int d, int x, vector<int> &di) {
if (d > tt) {
di.pb(x);
return;
}
for (int i = 0; i <= ::d[d].scd; ++i, x *= ::d[d].fst) {
dfs(d + 1, x, di);
}
}
inline vector<int> getd(int n) {
vector<int> di;
tt = 0;
while (n > 1) {
int t = mpr[n], k = 0;
while (n % t == 0) {
n /= t;
++k;
}
d[++tt] = pii(t, k);
}
dfs(1, 1, di);
return di;
}
inline int calc(int n, int m) {
auto dn = getd(n), dm = getd(m);
int ans = 0;
for (int x : dn) {
for (int y : dm) {
int z = x * y / __gcd(x, y);
fix(ans += 1ULL * phi[x] * phi[y] * qpow(c[z], n * m / z) % mod - mod);
}
}
ans = 1ULL * ans * inv[n * m] % mod;
return ans;
}
struct node {
int n, m, q, x, y, l, w;
node(int _n = 0, int _m = 0, int _q = 0, int _x = 0, int _y = 0, int _l = 0, int _w = 0) : n(_n), m(_m), q(_q), x(_x), y(_y), l(_l), w(_w) {}
} f[99999];
inline bool check(node &a, int x, int y) {
x %= a.n;
y %= a.m;
if (x % a.x) {
return 0;
}
int t = x / a.x;
return a.y * t % a.m == y;
}
inline bool check(node &a, node &b) {
if (a.n % b.n || a.m % b.m) {
return 0;
}
return check(b, a.x, a.y);
}
void solve() {
n = read();
m = read();
K = read();
for (int i = 1; i <= K; ++i) {
a[i] = read();
}
for (int i = 1; i <= K; ++i) {
if (vis[i]) {
continue;
}
int cnt = 0, u = i;
do {
++cnt;
vis[u] = 1;
u = a[u];
} while (u != i);
b[cnt] += cnt;
}
for (int i = 1; i <= n * m; ++i) {
for (int j = i; j <= n * m; j += i) {
c[j] += b[i];
}
}
mems(vis, 0);
phi[1] = inv[1] = 1;
for (int i = 2; i <= n * m; ++i) {
inv[i] = 1ULL * (mod - mod / i) * inv[mod % i] % mod;
}
for (int i = 2; i <= n * m; ++i) {
if (!vis[i]) {
pr[++tot] = i;
mpr[i] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= tot && i * pr[j] <= n * m; ++j) {
vis[i * pr[j]] = 1;
mpr[i * pr[j]] = pr[j];
if (i % pr[j] == 0) {
phi[i * pr[j]] = phi[i] * pr[j];
break;
}
phi[i * pr[j]] = phi[i] * (pr[j] - 1);
}
}
tot = 0;
for (int u = 1; u <= n; ++u) {
if (n % u) {
continue;
}
for (int v = 1; v <= m; ++v) {
if (m % v) {
continue;
}
int s = __gcd(u, v);
for (int q = 0; q < s; ++q) {
int g = __gcd(q, s);
int x = u / s * g, y = v / s * q;
int t1 = __gcd(s, __gcd(x, y));
int cnt = u * v / s * g, t2 = cnt / t1;
f[++tot] = node(u, v, q, x, y, cnt, calc(t1, t2));
}
}
}
sort(f + 1, f + tot + 1, [&](const node &a, const node &b) {
return a.l < b.l;
});
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= tot; ++i) {
for (int j = 1; j < i; ++j) {
if (check(f[i], f[j])) {
fix(f[i].w -= f[j].w);
}
}
fix(ans1 += 1ULL * f[i].w * f[i].l % mod - mod);
fix(ans2 += f[i].w - mod);
}
writeln(ans1);
writeln(ans2);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
:::