BZOJ3812: 主旋律

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/oi_Konnyaku/article/details/84785131

传送门

Sol

考虑容斥
强联通图反过来就是一些缩点后的 D A G DAG
一个套路就是对出(入)度为 0 0 的点进行容斥
g S , h S g_S,h_S 分别表示选了奇数个 0 0 入度和偶数个的,集合为 S S 的方案数
那么通过钦定一个特殊的点 u u
g S = T S , u T f T h S T g_S=\sum_{T\subset S,u \in T}f_Th_{S-T}
h S = T S , u T f T g S T h_S=\sum_{T\subset S,u \in T}f_Tg_{S-T}
那么考虑容斥求出 f f ,由于 g S g_S 包含 f S f_S ,而且 f S f_S 合法,所以容斥的时候 g S g_S 不能包括 f S f_S
那么
f S = 2 E { S } + T S , T S ( h T g T ) 2 E { S T } + E { T } > { S T } f_S=2^{|E_{\{S\}}|}+\sum_{T\subset S,T\ne S}(h_T-g_T)2^{E_{\{S-T\}}+E{_{\{T\}->\{S-T\}}}}
这里的 g S g_S 不包括 f S f_S E E 表示边集, { S } > { T } \{S\}->\{T\} 即集合 S S T T 的边

可以通过把子集弄出来做优化到 Θ ( 3 n ) \Theta(3^n)
不过我没有写QwQ

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

const int mod(1e9 + 7);

int n, m, f[1 << 15], g[1 << 15], h[1 << 15], cnt[1 << 15], to[20], e[1 << 15], pw[300];

inline void Inc(int &x, int y) {
	if ((x += y) >= mod) x -= mod;
}

int main() {
	register int i, a, b, t, s, j;
	scanf("%d%d", &n, &m), t = 1 << n;
	for (i = 1; i <= m; ++i) scanf("%d%d", &a, &b), --a, --b, to[a] |= 1 << b;
	for (i = 1; i < t; ++i) cnt[i] = cnt[i >> 1] + (i & 1);
	for (pw[0] = i = 1; i <= m; ++i) {
		pw[i] = pw[i - 1] << 1;
		if (pw[i] >= mod) pw[i] -= mod;
	}
	for (i = 0; i < t; ++i)
		for (j = 0; j < n; ++j) if (i >> j & 1) e[i] += cnt[to[j] & i];
	for (i = 0; i < n; ++i) f[1 << i] = g[1 << i] = 1;
	for (i = 1; i < t; ++i)
		if (cnt[i] > 1) {
			f[i] = pw[e[i]];
			for (a = 0; a < n; ++a) if (i >> a & 1) break;
			for (j = (i - 1) & i; j; j = (j - 1) & i) {
				for (s = b = 0; b < n; ++b) if (i >> b & 1) s += cnt[to[b] & (i ^ j)];
				Inc(f[i], 1LL * (h[j] - g[j] + mod) * pw[s] % mod);
				if (j >> a & 1) {
					Inc(g[i], 1LL * f[j] * h[i ^ j] % mod);
					Inc(h[i], 1LL * f[j] * g[i ^ j] % mod);
				}
			}
			Inc(f[i], (h[i] - g[i] + mod) % mod), Inc(g[i], f[i]);
		}
	printf("%d\n", f[t - 1]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/oi_Konnyaku/article/details/84785131
今日推荐