题意
传送门 AtCoder 327G Many Good Tuple Problems
题解
将 ( A i , B i ) (A_i, B_i) (Ai,Bi) 看作一条边并建图,序列对满足条件当且仅当所构造的图为二分图。
令
x
(
n
,
k
)
x(n,k)
x(n,k) 代表代标号的节点数为
n
n
n 且边数为
k
k
k 的简单二分图的方案数;
b
(
m
,
k
)
b(m,k)
b(m,k) 代表将
m
m
m 条边依次分配到简单图的
k
k
k 条边上的方案数。根据容斥原理,考虑简单图上部分边没有分配
m
m
m 条边中至少一条的情况,可以得到
b
(
m
,
k
)
=
∑
i
=
0
k
(
−
1
)
i
(
k
−
i
)
m
(
k
i
)
b(m,k)=\sum\limits_{i=0}^{k}(-1)^{i}(k-i)^{m}\binom{k}{i}
b(m,k)=i=0∑k(−1)i(k−i)m(ik)
对于
x
(
n
,
k
)
x(n,k)
x(n,k) 的求解,观察到联通二分图的染色方案只有两种,那么先求出可以黑白染色的二分连通图数量,最后像背包问题一样不断累加联通分量即可。具体而言,令
g
(
n
,
k
)
g(n,k)
g(n,k) 为将图中节点黑白染色后,相同颜色的节点间不存在连边的方案数;
f
(
n
,
k
)
f(n,k)
f(n,k) 则为
g
(
n
,
k
)
g(n,k)
g(n,k) 中联通图的数量,则有
g
(
n
,
k
)
=
∑
i
=
0
n
(
n
i
)
(
i
(
n
−
i
)
k
)
g(n,k) = \sum\limits_{i=0}^{n}\binom{n}{i}\binom{i(n-i)}{k}
g(n,k)=i=0∑n(in)(ki(n−i))
枚举图中编号最小的节点所在联通分量的情况,则可以容斥得到
f
(
n
,
k
)
=
g
(
n
,
k
)
−
∑
i
=
1
n
−
1
∑
j
=
0
k
(
n
−
1
i
−
1
)
f
(
i
,
j
)
g
(
n
−
i
,
k
−
j
)
f(n,k)=g(n,k) - \sum\limits_{i=1}^{n-1}\sum\limits_{j=0}^{k}\binom{n-1}{i-1}f(i,j)g(n-i,k-j)
f(n,k)=g(n,k)−i=1∑n−1j=0∑k(i−1n−1)f(i,j)g(n−i,k−j)
类似背包操作,逆容斥回去得到
x
(
n
,
k
)
=
f
(
n
,
k
)
/
2
+
∑
i
=
1
n
−
1
∑
j
=
0
k
(
n
−
1
i
−
1
)
f
(
i
,
j
)
/
2
⋅
x
(
n
−
i
,
k
−
j
)
x(n,k) = f(n,k)/2 + \sum\limits_{i=1}^{n-1}\sum\limits_{j=0}^{k}\binom{n-1}{i-1}f(i,j)/2\cdot x(n-i,k-j)
x(n,k)=f(n,k)/2+i=1∑n−1j=0∑k(i−1n−1)f(i,j)/2⋅x(n−i,k−j)
考虑
(
A
i
,
B
i
)
(A_i, B_i)
(Ai,Bi) 的有序性,每一条边贡献为
2
2
2,总贡献为
2
m
2^m
2m,则答案为
2
m
⋅
∑
k
=
0
l
x
(
n
,
k
)
b
(
m
,
k
)
2^m\cdot\sum\limits_{k=0}^{l}x(n,k)b(m,k)
2m⋅k=0∑lx(n,k)b(m,k)
其中
l
l
l 为二分图边数的最大值,其等于
⌊
n
/
2
⌋
⋅
⌈
n
/
2
⌉
\lfloor n/2\rfloor\cdot\lceil n/2\rceil
⌊n/2⌋⋅⌈n/2⌉。总时间复杂度
O
(
n
4
log
m
+
n
6
)
O(n^4\log m + n^6)
O(n4logm+n6)。
#include <bits/stdc++.h>
using namespace std;
constexpr int MOD = 998244353;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
auto power = [&](ll x, int n) {
ll res = 1;
while (n > 0) {
if (n & 1) {
(res *= x) %= MOD;
}
(x *= x) %= MOD, n >>= 1;
}
return res;
};
int l = max(n, (n / 2) * ((n + 1) / 2));
vector<vector<ll>> c(l + 1, vector<ll>(l + 1));
for (int i = 0; i <= l; ++i) {
c[i][0] = 1;
}
for (int i = 0; i < l; ++i) {
for (int j = 0; j < l; ++j) {
c[i + 1][j + 1] = (c[i][j + 1] + c[i][j]) % MOD;
}
}
vector<ll> b(l + 1);
for (int k = 0; k <= l; ++k) {
for (int i = 0; i <= k; ++i) {
b[k] += ((i & 1) ? -1 : 1) * power(k - i, m) * c[k][i] % MOD;
b[k] %= MOD;
}
}
vector<vector<ll>> g(n + 1, vector<ll>(l + 1));
for (int nd = 0; nd <= n; ++nd) {
for (int k = 0; k <= l; ++k) {
for (int i = 0; i <= nd; ++i) {
g[nd][k] += c[nd][i] * c[i * (nd - i)][k] % MOD;
g[nd][k] %= MOD;
}
}
}
vector<vector<ll>> f(n + 1, vector<ll>(l + 1));
for (int nd = 0; nd <= n; ++nd) {
for (int k = 0; k <= l; ++k) {
f[nd][k] = g[nd][k];
for (int i = 1; i < nd; ++i) {
for (int j = 0; j <= k; ++j) {
f[nd][k] -= f[i][j] * c[nd - 1][i - 1] % MOD * g[nd - i][k - j] % MOD;
f[nd][k] %= MOD;
}
}
}
}
ll inv2 = power(2, MOD - 2);
vector<vector<ll>> x(n + 1, vector<ll>(l + 1));
for (int nd = 0; nd <= n; ++nd) {
for (int k = 0; k <= l; ++k) {
x[nd][k] = f[nd][k] * inv2 % MOD;
for (int i = 1; i < nd; ++i) {
for (int j = 0; j <= k; ++j) {
x[nd][k] += f[i][j] * inv2 % MOD * c[nd - 1][i - 1] % MOD * x[nd - i][k - j] % MOD;
x[nd][k] %= MOD;
}
}
}
}
ll res = 0;
for (int k = 0; k <= l; ++k) {
res += x[n][k] * b[k] % MOD;
res %= MOD;
}
(res += MOD) %= MOD;
(res *= power(2, m)) %= MOD;
cout << res << '\n';
return 0;
}