Time limit: 3 s
Memory limit: 512 MiB
In a bipartite graph, nodes are partitioned into two disjoint sets A and B such that every edge connects anode from A with a node from B. A matching M is a set of edges where no two edges share a common node. We say that a matching M blankets a set of nodes V if every node in V is an endpoint of at least one edge in M.
We are given a bipartite graph where each node is assigned a weight — a positive integer. Weight of a set of nodes is simply the sum of the weights of the individual nodes.
Given an integer threshold t, find the number of different sets of nodes V such that the weight of V is at least t and V is blanketed by at least one matching M.
Input
The first line contains two integers n and m (1 ≤ n, m ≤ 20) — the number of nodes in A and B respectively. Let us denote the nodes of A with a1, a2, . . . , an and the nodes of B with b1, b2, . . . , bm.
The following n lines contain m characters each that describe the edges of the graph. The j-th character in the i-th line is “1” if there is an edge between ai and bj and “0” otherwise.
The following line contains n integers v1, v2, . . . , vn (1 ≤ vk ≤ 10 000 000) — the weights of the nodes a1, a2, . . . an.
The following line contains m integers w1, w2, . . . , wm (1 ≤ wk ≤ 10 000 000) — the weights of the nodes b1, b2, . . . bm. The following line contains an integer t (1 ≤ t ≤ 400 000 000) — the weight threshold.
Output
Output the number of sets of nodes whose weight is at least t and are blanketed by at least one matching M.
有一个二分图,把一个能够被某一合法匹配完全覆盖的点集当做合法的。
问这些合法的点集当中,有多少个权值和大于t.
Hall定理:
X或Y集合其中一个集合所有点都被匹配了,X集合有n个点。
那么二分图G存在完美匹配,则取任意正整数1<=k<=n,均满足我从X集合选出k个不同的点,那么它们连向的y集合的点个数不小于k。
根据Hall定理枚举两个集合当中点的状态(取或不取),将所有可能点集的权值和求出,双指针lower_bound累加得到答案。点集用状态压缩表示,DP转移。
#include <cstdio>
#include <iostream>
#include <string.h>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <vector>
#include <set>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <stack>
#include <iomanip>
#define mem0(a) memset(a,0,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
const int maxn=25,maxk=(1<<20)+5,inf=0x3f3f3f3f;
const ll llinf=0x3f3f3f3f3f3f3f3f;
const ld pi=acos(-1.0L);
ll a[maxn],b[maxn];
ll ax[maxk],bx[maxk],aw[maxk],bw[maxk];
bool af[maxk],bf[maxk];
ll cnt[maxk];
char s[maxn][maxn];
int main() {
int n,m,na,nb,i,j;
scanf("%d%d",&n,&m);
for (i=0;i<n;i++) {
scanf("%s",s[i]);
for (j=0;j<m;j++)
if (s[i][j]=='1') aw[1<<i]|=1<<j,bw[1<<j]|=1<<i;
}
for (i=0;i<n;i++)
scanf("%lld",&a[i]);
for (i=0;i<m;i++)
scanf("%lld",&b[i]);
cnt[0]=0;
for (i=1;i<max(1<<n,1<<m);i++)
cnt[i]=cnt[i/2]+i%2;
ll t;
scanf("%lld",&t);
na=nb=0;
for (i=0;i<(1<<n);i++) {
ll sum=0;
af[i]=1;
for (j=0;j<n;j++) {
if ((i>>j)%2==1) {
sum+=a[j]; //当前状态的点的权值和
aw[i]=aw[i]|aw[i^(1<<j)]; //对应另一组点的状态
af[i]=af[i]&af[i^(1<<j)]; //状态合法性转移
}
}
af[i]=af[i]&&(cnt[i]<=cnt[aw[i]]);
if (af[i])
ax[na++]=sum;
}
for (i=0;i<(1<<m);i++) {
ll sum=0;
bf[i]=1;
for (j=0;j<m;j++) {
if ((i>>j)%2==1) {
sum+=b[j];
bw[i]=bw[i]|bw[i^(1<<j)];
bf[i]=bf[i]&bf[i^(1<<j)];
}
}
bf[i]=bf[i]&&(cnt[i]<=cnt[bw[i]]);
if (bf[i])
bx[nb++]=sum;
}
sort(ax,ax+na);
ll ans=0;
for (i=0;i<nb;i++) {
ans+=na-(lower_bound(ax,ax+na,t-bx[i])-ax);
}
printf("%lld\n",ans);
return 0;
}