T392582 我有抑郁症【题解】
题目描述
要求有多少个序列满足:
- 令 \(v=1\sim n\)
- 从 \(v\) 号点开始,走到 \(p_v\),…,最后走回 \(v\)
- 记录每个点被走到的次数(起点算,终点不算,反正只算一次)
- \(i\) 号点走到的次数恰好是 \(i\)
答案对 \(998,244,353\) 取模
Solution
首先,一个点有一个出边,这个图为什么一定可以走回来?
因为这是个排列啊!不能有相同的入边,每个点只有一个出边,这是什么!这是环啊!
所以整个图就是若干个环,我们要在环上计数。
设我们当前的 \(a_i\) 统称为 \(k\),\(a_i=k\) 的点数量是 \(c_k\),则这个图有答案当且仅当 \(k | c_k\),为什么?
因为一个环上 \(a_i=k\),那么这个环只有 \(k\) 个点,\(a_i=k\) 的图有可能会有很多个由 \(k\) 个点组成的环,所以必须要 \(k\) 能被 \(c_k\) 整除
首先考虑这个图上有 \(\cfrac{c_k}{k}\) 个环,为了好描述,我们令 \(\cfrac{c_k}{k}=S\),那么我们先从大的角度入手,即分给每个环多少个元素
那肯定是从 \(c_k\) 个点选 \(k\) 个了,然后再从剩下 \(c_k-k\) 个点选 \(k\) 个,以此类推,即贡献为
\[\cfrac{\binom{c_k}{k}\binom{c_k-k}{k} …\binom{k}{k}}{S!}
\]
然后在考虑每个环里的贡献,即有多少种不同的环可以符合条件
我一开始想 \(1\) 号点不能在第一个,好难搞啊
但是后面大佬们告诉我,不要考虑细节,直接构造一个环就完事了,想想也确实是这样
因为你这个序列肯定是个环,因为具体在 \(1\) 号点填的是这条出边的终点,所以相当于求本质不同的环的个数!!!
所以就是圆排列,也就是 \((k-1)!\) 了,然后有 \(S\) 组,相乘就好了
也就是贡献是:
\[\cfrac{\binom{c_k}{k}\binom{c_k-k}{k} …\binom{k}{k}}{S!} \times (k-1)!^{S}
\]
做完了,以下是代码
#include <bits/stdc++.h>
using namespace std;
int T;
const int MAXN = 5e5 + 7;
typedef long long ll;
const ll Mod = 998244353;
ll Fac[MAXN], Inv[MAXN];
int n;
ll qpow(ll a, ll b) {
ll ans = 1;
while (b) {
if (b & 1) ans = ans * a % Mod;
a = a * a % Mod, b >>= 1;
}
return ans;
}
int A[MAXN];
ll ans = 1;
ll C(int n, int m) { return Fac[n] * Inv[m] % Mod * Inv[n - m] % Mod; }
int main () {
ios :: sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
cin >> T;
Fac[0] = 1;
for (int i = 1; i <= 500000; i ++) Fac[i] = Fac[i - 1] * i % Mod;
Inv[500000] = qpow(Fac[500000], Mod - 2);
for (int i = 500000 - 1; i >= 0; i --) Inv[i] = Inv[i + 1] * (i + 1) % Mod;
while (T --) {
cin >> n; memset(A, 0, sizeof(A)); ans = 1;
for (int i = 1, x; i <= n; i ++) cin >> x, A[x] ++;
for (int i = 2; i <= n; i ++) {
if (A[i] % i && A[i]) ans = 0;
else if (A[i]){
for (int j = 0; A[i] - j * i > 0; j ++) ans = ans * C(A[i] - i * j, i) % Mod;
ans = ans * Inv[A[i] / i] % Mod * qpow(Fac[i - 1], (A[i] / i)) % Mod;
}
}
cout << ans << '\n';
}
return 0;
}
完结撒花✿✿ヽ(°▽°)ノ✿