1. 程式人生 > 其它 >【題解】[CERC2016]二分毯 Bipartite Blanket

【題解】[CERC2016]二分毯 Bipartite Blanket

前置知識:霍爾定理

對於二分圖,兩部分別為 \(X,Y\),令 \(|X| \le |Y|\),則存在大小為 \(X\) 的匹配的充要條件是對於任意 \(S\subseteq X\),都有 \(|F(S)| \ge |S|\),其中 \(F(S) = \{y\ |\ x\to y \in E,x\in S,y\in Y\}\)

說人話就是對於 \(X\) 的所有子集,相鄰的點數都不小於子集。

必要性顯然,充分性不顯然,反證一下。

假設滿足條件但是匹配 \(<|X|\),那麼不妨令沒有匹配的點為 \(x\)\(x\) 的所有鄰居都被匹配了,此時從 \(x\) 出發一定存在一條增廣路(匈牙利演算法),如果不存在,則假設這條非增廣路經過 \(X\)

的點集為 \(X'\)\(|F(X')|\) 一定 \(<|X'|\),手動畫圖即可理解。

那麼對於本題關鍵結論是

\(V\)\(X\) 中的點集為 \(S\)\(Y\) 中的點集為 \(T\)。如果存在匹配覆蓋 \(S\),並存在匹配覆蓋 \(T\),則一定存在匹配覆蓋 \(V\)

這結論挺好猜的,\(N\le 20\) 顯然就是折半然後雙指標嘛。證明也不難。

\(S\)\(T\) 中的鄰居為 \(W\)\(S\) 和它的鄰居匹配,然後 \(T/W\) 再進行匹配即可。

所以直接狀壓 DP,\(f_S\) 表示集合 \(S\) 的鄰居集合,\(h_{S}\)

表示集合 \(S\) 是否合法,直接轉移 \(\mathcal{O}(n^22^n + m^22 ^m)\)

#define N 20
#define M ((1 << N) + 5)
int n, m, u[M], v[M], f[M], bt[M], g[M], p[M], q[M], l, r, h[M], a[M], b[M]; char s[N + 1];
int main() {
	read(n, m);
	rep(i, 0, n - 1){
		scanf("%s", s);
		rep(j, 0, m - 1)if(s[j] == '1')u[1 << i] |= 1 << j, v[1 << j] |= 1 << i;
	}
	rep(i, 0, n - 1)read(p[1 << i]);
	rep(i, 0, m - 1)read(q[1 << i]);
	int w = (1 << max(n, m)) - 1;
	rp(i, w)bt[i] = bt[i >> 1] + (i & 1);
	w = (1 << n) - 1;
	h[0] = 1;
	rp(i, w){
		int j = i & -i;
		p[i] = p[j] + p[i ^ j];
		f[i] = u[j] | f[i ^ j];
		h[i] = 1;
		for(int k = i; k; k -= k & -k)h[i] &= h[i ^ (k & -k)];
		h[i] &= bt[f[i]] >= bt[i];
		if(h[i])a[++l] = p[i];
	}a[++l] = 0;
	w = (1 << m) - 1;
	rp(i, w){
		int j = i & -i;
		q[i] = q[j] + q[i ^ j];
		g[i] = v[j] | g[i ^ j];
		h[i] = 1;
		for(int k = i; k; k -= k & -k)h[i] &= h[i ^ (k & -k)];
		h[i] &= bt[g[i]] >= bt[i];
		if(h[i])b[++r] = q[i];
	}b[++r] = 0;
	sort(a + 1, a + l + 1),
	sort(b + 1, b + r + 1);
	read(w);
	int j = r; LL ans = 0;
	rp(i, l){
		while(j && b[j] + a[i] >= w)j--;
		ans += r - j;
	}printf("%lld\n", ans);
	return 0;
}