P4708 画画 题解
可以枚举
而每条边又分在相同置换和不同置换内两种
相同置换内求不动点个数
在一个置换内的不动点个数是
不同置换内求不动点个数
考虑两个置换的大小为
而这些等价类可以出现可以不出现,对答案的贡献是
发现只与置换的大小有关,暴力枚举拆分,算每个拆分的方案数,先有一个排列的方案数
考虑欧拉回路这个更强的限制,即每个点的度数为偶数,同样考虑一个置换内的边。
- 若长度为奇数
2\times l+1 ,所有置换出不出现对点都是偶数的影响,可以不管。 - 若长度为偶数
2\times l ,其中l-1 个不动点对奇偶性是没有影响的,而最后一个取所有对角线的不动点会使所有点度数加1 。
考虑一个置换间的边
对一个
我们发现现在可以先将贡献乘上没有影响的
然后可以有
然后就是求使得满足限制的方案数,可以高斯消元异或矩阵,贡献为最后的自由元个数。
但还有更好的方法。
我们对现在
发现根和树边对应着唯一的可行解。
点数为
方案数就是
用并查集维护即可。
主要就是求不动点个数,考虑在一个置换内或置换间的不动点,暴力枚举拆分数乘上方案数,通过一个生成树的构造快速求得可行解个数,挺巧妙的。
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int Mod = 998244353;
int add (int a, int b) {
return a + b >= Mod ? a + b - Mod : a + b;
}
int mul (int a, int b) {
return 1ll * a * b % Mod;
}
int dec (int a, int b) {
return a - b < 0 ? a - b + Mod : a - b;
}
int ksm (int a, int b) {
int ans = 1;
for (; b; b >>= 1, a = mul (a, a)) if (b & 1) ans = mul (ans, a);
return ans;
}
void Add (int &a, int b) {
a = add (a, b);
}
cs int N = 55;
int gcd (int a, int b) {
return !b ? a : gcd (b, a % b);
}
int n, ans, inv[N], g[N][N];
int fa[N], sm[N];
int find (int x) {
return x == fa[x] ? x : fa[x] = find (fa[x]);
}
int loop[N], ct[N], now;
int calc() { //计算
int res = 0;
for (int i = 1; i <= now; i++) fa[i] = i, sm[i] = 0;
for (int i = 1; i <= now; i++) {
int v = loop[i];
res += ((v - 1) >> 1);
if (! (v & 1)) sm[i] = 1;
}
for (int i = 1; i <= now; i++) {
for (int j = i + 1; j <= now; j++) {
int u = loop[i], v = loop[j], gc = g[u][v];
int a = (u / gc) & 1, b = (v / gc) & 1;
if (a && b) fa[find (i)] = find (j), res += gc;
if (a && !b) sm[j] += gc;
if (!a && b) sm[i] += gc;
if (!a && !b) res += gc;
}
}
for (int i = 1; i <= now; i++)
if (find (i) == i) {
++res;
res += sm[i] == 0 ? 0 : sm[i] - 1;
} else sm[find (i)] += sm[i];
res -= now;
return ksm (2, res);
}
void dfs (int las, int rest, int coef) { //dfs枚举
if (!rest) {
Add (ans, mul (coef, calc()));
return;
}
for (int i = min (las, rest); i; i--) {
++ct[i];
loop[++now] = i;
dfs (i, rest - i, mul (coef, mul (inv[i], inv[ct[i]])));
--ct[i];
--now;
}
}
int main() {
cin >> n;
inv[0] = inv[1] = 1;
for (int i = 2; i <= n; i++) inv[i] = mul (Mod - Mod / i, inv[Mod % i]);
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) g[i][j] = gcd (i, j);
dfs (n, n, 1);
cout << ans;
return 0;
}