IOI2021集训队作业 121MB Bipartite Blanket

给你一个二分图,问满足下列条件的点集\(V\)的个数:

  1. 存在一个匹配覆盖\(V\)
  2. \(V\)中所有点的权值和大于等于一个定值。

\(n,m\le 20\)


对Hall定理的奇妙推广。

Hall定理:如果一个二分图存在完备匹配,当且仅当:对于一边的所有点集,临集大小大于等于点集大小。

必要性显然。充分性考虑不是完备匹配就存在增广路。

按照这个思路推广:如果有两边各有个集合\(S\)\(T\),如果\(S\)\(T\)都满足Hall定理,那么一定存在一个匹配覆盖\(S\bigcup T\)

具体证明是类似于上面那样找增广路。

根据这个性质处理一下然后双指针搞一搞。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 20
#define ll long long
int e[N];
int cnt[1<<N];
struct Work{
	int n;
	int a[N],v[N];
	int f[1<<N];
	ll s[1<<N];
	bool ok[1<<N];
	int q[1<<N|1],m;
	void init(){
		f[0]=0,s[0]=0,ok[0]=1;
		for (int i=0;i<n;++i)
			for (int j=0;j<1<<i;++j){
				f[j|1<<i]=f[j]|a[i];
				s[j|1<<i]=s[j]+v[i];
			}
		for (int i=0;i<1<<n;++i){
			ok[i]=(cnt[f[i]]>=cnt[i]);
			for (int t=i;t && ok[i];t-=t&-t)
				ok[i]&=ok[i-(t&-t)];
			if (ok[i])
				q[++m]=s[i];
		}
		sort(q+1,q+m+1);
	}
} L,R;
int main(){
//	freopen("in.txt","r",stdin);
	cnt[0]=0;
	for (int i=1;i<1<<N;++i)	
		cnt[i]=cnt[i>>1]+(i&1);
	scanf("%d%d",&L.n,&R.n);
	for (int i=0;i<L.n;++i){
		static char str[N+3];
		scanf("%s",str);
		for (int j=0;j<R.n;++j){
			L.a[i]|=str[j]-'0'<<j;
			R.a[j]|=str[j]-'0'<<i;
		}
	}
	for (int i=0;i<L.n;++i) scanf("%d",&L.v[i]);
	for (int i=0;i<R.n;++i) scanf("%d",&R.v[i]);
	L.init(),R.init();
	ll ans=0;
	int t;
	scanf("%d",&t);
	for (int i=1,j=R.m;i<=L.m;++i){
		while (j && L.q[i]+R.q[j]>=t)
			--j;
		ans+=R.m-j;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2020-10-19 21:35  jz_597  阅读(210)  评论(0编辑  收藏  举报