1
先拆贡献,把边的贡献拆到点上,发现是出度减入度,再乘一个可以自己挑选的系数。那么把点按出度减入度的绝对值排序,双方一定依次选最前面的。也可以发现确定数值的点的贡献一定为 \(0\),因为所有方案对称。
dp,按 \(|\text{out}-\text{in}|\) 排序后跑背包,具体而言,对于每种 \((in, out)\),枚举其数量,以确定它对答案的贡献是 \(-1/0/1\),一边维护 \(f_{i, j, k}\) 表示前 \(i\) 种物品共填充长度为 \(j\) 的入度数组和长度为 \(k\) 的出度数组,的方案数,转移时重复部分用组合数(两维独立,同种点的出现 \(in\) 次不区分,把 \(x\cdot in\) 个数和 \(j\) 个数有顺序合并)。 把确定数值的点挑出来,它们可以任意补充剩余的出入度,卷一下就行。
写不动了,理解了一下 hz 的代码。
#include <bits/stdc++.h>
using namespace std;
const int N = 55, mod = 1e9 + 7, iv2 = (mod + 1) / 2;
int n, m, c[N];
void add(auto &x, auto y) {x = (x + y) % mod; }
struct node{
int ind, oud;
}a[N * N];
int tot;
int power(int x, int y) {
int ret = 1;
while (y) {
if (y & 1) ret = 1ll * ret * x % mod;
x = 1ll * x * x % mod, y >>= 1;
}
return ret;
}
int get_inv(int x) {return power(x, mod - 2); }
int fac[N], inv[N], C[N][N];
int p[N][N], f[2][N][N][N], g[2][N][N][N];
void init() {
fac[0] = inv[0] = 1;
for (int i=1; i<=m; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
inv[m] = get_inv(fac[m]);
for (int i=m-1; i; --i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
for (int i=0; i<=n; ++i) {
C[i][0] = 1;
for (int j=1; j<=i; ++j)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
p[0][0] = 1;
for (int i=1; i<=n; ++i)
for (int j=0; j<=m; ++j)
for (int k=0; j+k<=m; ++k)
add(p[i][j + k], 1ll * p[i - 1][j] * inv[k]);
for (int i=0; i<=m; ++i)
for (int j=0; j<=m; ++j) a[++tot] = (node) {i, j};
sort(a + 1, a + tot + 1, [](node x, node y) {
return abs(x.ind - x.oud) > abs(y.ind - y.oud);
});
g[0][0][0][0] = 1;
for (int i=1; i<=tot; ++i) {
int l = (i & 1), r = (l ^ 1);
int mul = 1ll * inv[a[i].ind] * inv[a[i].oud] % mod;
for (int k=0; k<=n; ++k)
for (int in=0; in<=m; ++in)
for (int out=0; out<=m; ++out) {
if (!g[r][k][in][out]) continue;
for (int x=0,pw=1; k+x<=n&&in+x*a[i].ind<=m&&out+x*a[i].oud<=m; ++x,pw=1ll*pw*mul%mod) {
add(g[l][k + x][in + x * a[i].ind][out + x * a[i].oud], 1ll * pw * g[r][k][in][out] % mod * C[k + x][x]);
add(f[l][k + x][in + x * a[i].ind][out + x * a[i].oud], 1ll * pw * f[r][k][in][out] % mod * C[k + x][x]);
if (x & 1) {
int tmp = std::abs(a[i].ind - a[i].oud);
if ((k + x) % 2 == 0) tmp = mod - tmp;
add(f[l][k + x][in + x * a[i].ind][out + x * a[i].oud], 1ll * pw * g[r][k][in][out] % mod * C[k + x][x] % mod * tmp);
}
}
g[r][k][in][out] = f[r][k][in][out] = 0;
}
}
}
int main() {
cin >> n >> m;
for (int i=1; i<=n; ++i) cin >> c[i];
init();
for (int i=1,cnt=0; i<=n; ++i) {
cnt += c[i] == 2;
for (int j=1; j<=m; ++j) {
//printf("calc %d %d\n", i, j);
int ans = 0;
for (int in=0; in<=j; ++in)
for (int out=0; out<=j; ++out) {
add(ans, 1ll * f[tot & 1][cnt][in][out] * p[i - cnt][j - in] % mod * p[i - cnt][j - out]);
//printf("add in %d out %d f %d p %d * %d\n", in, out, f[tot & 1][cnt][in][out], p[i - cnt][j - in], p[i - cnt][j - out]);
}
ans = 1ll * ans * fac[j] % mod * fac[j] % mod;
cout << 1ll * ans * iv2 % mod << " ";
}
cout << "\n";
}
return 0;
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/18845595

浙公网安备 33010602011771号