状态压缩dp,F[mask]表示一边的点集mask是否存在完备匹配(左右各做一次),依据Hall定理转移
将左右所有存在完备匹配的点集合的权值和算出来塞到两个数组里,two pointers判权值和够不够就行了
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define tn ((1<<n)-1)
#define tm ((1<<m)-1)
#define L (1<<(i-1))
#define R (1<<(j-1))
#define M 1100000
#define N 50
using namespace std;
typedef long long LL;
int F[M],G[M],f[M],g[M],c[M],w[N],v[N],n,m,t,p,q,h,_,i,j;
LL P[M],Q[M],ans,s;
char mp[N][N];
inline int rd() { int r; scanf("%d",&r); return r; }
int main() {
freopen("guard.in","r",stdin);
freopen("guard.out","w",stdout);
n=rd(),m=rd();
for (i=1;i<=n;i++) scanf("%s",mp[i]+1);
for (i=1;i<=n;w[i++]=rd());
for (i=1;i<=m;v[i++]=rd());
t=rd();
for (i=1;i<=n;++i)
for (j=1;j<=m;++j)
mp[i][j]=='1' ? F[L]|=R,G[R]|=L :0;
for (_=1;_<=max(tn,tm);_++)c[_]=c[_-(_&-_)]+1;
for (_=0;_<=tn;++_,s=0) {
f[_]=1;
for (int i=1;i<=n;++i)
L&_ ? F[_]|=F[_^L],f[_]&=f[_^L],s+=w[i] :0;
(f[_]&=c[F[_]]>=c[_]) ? P[++p]=s :0;
}
for (_=0;_<=tm;++_,s=0) {
g[_]=1;
for (int j=1;j<=m;j++)
R&_ ? G[_]|=G[_^R],g[_]&=g[_^R],s+=v[j] :0;
(g[_]&=(c[G[_]]>=c[_])) ? Q[++q]=s :0;
}
sort(P+1,P+p+1),sort(Q+1,Q+q+1);
for (_=1,h=q+1;_<=p;++_,ans+=q-h+1)
while (h-1 && P[_]+Q[h-1]>=t) h--;
printf("%lld\n",ans);
return 0;
}