传送门 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
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;
}