Loading

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;
}
posted @ 2025-04-24 22:58  purplevine  阅读(42)  评论(0)    收藏  举报