IOI2021集训队作业 121MB Bipartite Blanket

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

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

(n,mle 20)


对Hall定理的奇妙推广。

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

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

按照这个思路推广:如果有两边各有个集合(S)(T),如果(S)(T)都满足Hall定理,那么一定存在一个匹配覆盖(Sigcup 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
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/13843277.html