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;
}
浙公网安备 33010602011771号