QOJ6275 大度一点

巧妙题,学习了。


考虑去动态添加限制。

\(f_{i,S}\) 表示点集 \(S\),我们约束了 \(\le i\) 的点的度数 \(\ge 2\),此时合法边集数。

考虑计算 \(f_{i,S}\)\(f_{i-1,S}\) 的变化。

如果 \(i\) 在边集中的度数 \(=0\),那么不合法的方案数就是 \(f_{i-1,S\backslash\left\{i\right\}}\)

如果 \(i\) 在边集中的度数 \(=1\),那么如果我们删掉 \(i\),就会有一连串的链 \((i=S_1\to S_2\to \cdots\to S_k)\to H\) 被删除,其中 \(H\) 是一个合法的子图,前面的 \(S_{2\sim k}\) 都是 \(<i\) 的。

考虑对这条链倒着 dp。我们从 \(H\) 中与 \(S_k\) 相连的点开始,每次往外拓展一个编号 \(\le i\) 的新点,拓展到 \(i\) 就停止。即 \(g_{S,j}\) 表示当前我们拓展出来的点集为 \(S\),当前链上的点为 \(j\) 的方案数。

那么 \(i\) 在边集中度数 \(=1\) 的方案就是 \(g_{S,i}\)。直接减掉即可。

总复杂度是 \(O(n^32^n)\) 的,因为要枚举 \(i\),对 \(g\) dp 为 \(O(n^22^n)\),不过跑得很快。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int mod = 998244353;
void Add(int &x, ll y) { x = (x + y) % mod; }
const int kN = 20, kM = 205, kS = (1 << 19) + 5;
int n, m, q;
int nbr[kN], pw2[kM];
int f[kS], g[kS][kN];

int main() {
//  freopen("1.in", "r", stdin);
//  freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  pw2[0] = 1;
  for(int i = 1; i <= m; i++) pw2[i] = 2 * pw2[i - 1] % mod;
  for(int i = 1; i <= m; i++) {
    int u, v;
    cin >> u >> v, u--, v--;
    nbr[u] |= (1 << v);
    nbr[v] |= (1 << u);
  }
  for(int i = 0; i < (1 << n); i++) {
    int e = 0;
    for(int t = i; t; t &= (t - 1)) {
      int p = __builtin_ctz(t);
      e += __builtin_popcount(nbr[p] & i);
    }
    f[i] = pw2[e / 2];
  }
  for(int i = 0; i < n; i++) {
    int cr = (i & 1), ls = !cr;
    memset(g, 0, sizeof(g));
    for(int s = 0; s < (1 << n); s++) {
      if((s >> i) & 1) continue;
      for(int t = s; t; t &= (t - 1)) {
        g[s][__builtin_ctz(t)] = f[s];
      }
    }
    for(int j = i + 1; j < n; j++) g[1 << j][j] = 1;
    for(int s = 0; s < (1 << n); s++) {
      if((s >> i) & 1) continue;
      for(int t = s; t; t &= (t - 1)) {
        int p = __builtin_ctz(t);
        if(!((s >> p) & 1)) continue;
        int out = nbr[p] & ~s;
        for(int tt = out; tt; tt &= (tt - 1)) {
          int j = __builtin_ctz(tt);
          if(j > i) break;
          Add(g[s | (1 << j)][j], g[s][p]);
        }
      }
    }
    for(int s = (1 << n) - 1; ~s; s--) {
      if((s >> i) & 1) {
        Add(f[s], 2 * mod - f[s ^ (1 << i)] - g[s][i]);
      }
    }
  }
  cin >> q;
  for(int i = 1; i <= q; i++) {
    int msk = 0;
    string s;
    cin >> s;
    for(int j = 0; j < n; j++) msk |= ((s[j] - '0') << j);
    cout << f[msk] << "\n";
  }
  return 0;
}
posted @ 2025-09-09 17:02  CJzdc  阅读(33)  评论(0)    收藏  举报