IOI2021集训队作业 121MB Bipartite Blanket
给你一个二分图,问满足下列条件的点集\(V\)的个数:
- 存在一个匹配覆盖\(V\)。
- \(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;
}