闻所未闻的$dp$神题(我不会的题)
令$f[S][i]$表示子集状态为$S$,且$S$中最大联通块恰好为$i$的方案数
考虑转移,我们枚举$S$中最小的元素$v$来转移,这样就能不重
$f[S][i] = \sum\limits_{T \in S \;and\;v \in T} f[T][...] * C[S \wedge T]$
由于这么递归转移不好确定后面的状态,因此我们可以递推转移,在代码中有所体现
$C[S]$表示将$S$联通的方案数
我们考虑容斥,用全集减去所有不联通的方案数,我们考虑枚举最小点$v$所在的集合
之后转移时$C[S] = 2^{E[S]} - \sum\limits_{T \in s \;ans\;v\; \in T} C[T] * 2^{E[S \wedge T]}$
其中,$E[S]$表示处于$S$内部的边的方案数
复杂度$O(3^n * n)$
#include <set> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define re register #define de double #define le long double #define ri register int #define ll long long #define sh short #define pii pair<int, int> #define mp make_pair #define pb push_back #define fi first #define se second #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > ‘9‘ || c < ‘0‘) { if(c == ‘-‘) w = -1; c = gc(); } while(c >= ‘0‘ && c <= ‘9‘) p = p * 10 + c - ‘0‘, c = gc(); return p * w; } int wr[50], rw; #define pc(iw) putchar(iw) tpr inline void write(ra o, char c = ‘\n‘) { if(!o) pc(‘0‘); if(o < 0) o = -o, pc(‘-‘); while(o) wr[++ rw] = o % 10, o /= 10; while(rw) pc(wr[rw --] + ‘0‘); pc(c); } tpr inline void cmin(ra &a, ra b) { if(a > b) a = b; } tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } tpr inline bool ckmin(ra &a, ra b) { return (a > b) ? a = b, 1 : 0; } tpr inline bool ckmax(ra &a, ra b) { return (a < b) ? a = b, 1 : 0; } } using namespace std; using namespace remoon; #define sid 17 #define mod 1000000007 inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; } inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; } inline int mul(int a, int b) { return 1ll * a * b % mod; } int pc[2555]; int u[555], v[555]; int E[(1 << 16) + 5000], C[(1 << 16) + 5000]; int f[(1 << 16) + 5000][sid]; int n, m; int main() { n = read(); m = read(); pc[0] = 1; rep(i, 1, 1000) pc[i] = mul(pc[i - 1], 2); rep(i, 1, m) u[i] = read(), v[i] = read(); rep(S, 0, (1 << n) - 1) rep(i, 1, m) if((S & (1 << u[i] - 1)) && (S & (1 << v[i] - 1))) ++ E[S]; C[0] = 1; rep(S, 1, (1 << n) - 1) { int res = pc[E[S]], mi = -1; rep(i, 1, n) if(S & (1 << i - 1)) { mi = i; break; } for(ri T = S & (S - 1); T; T = (T - 1) & S) if(T & (1 << mi - 1)) dec(res, mul(C[T], pc[E[S ^ T]])); C[S] = res; } f[0][0] = 1; rep(S, 0, (1 << n) - 1) rep(j, 0, n) if(f[S][j]) { int mi = -1; rep(i, 1, n) if(!(S & (1 << i - 1))) { mi = i; break; } if(mi == -1) continue; int D = ((1 << n) - 1) ^ (S | (1 << mi - 1)); for(ri T = D; ; T = (T - 1) & D) { int st = __builtin_popcount(T | (1 << mi - 1)); inc(f[S | (1 << mi - 1) | T][max(j, st)], mul(f[S][j], C[(1 << mi - 1) | T])); if(!T) break; } } rep(i, 1, n) write(f[(1 << n) - 1][i]); return 0; }
原文地址:https://www.cnblogs.com/reverymoon/p/9800998.html
时间: 2024-11-14 06:23:44