当前位置: 首页 > 工具软件 > Blanket > 使用案例 >

[CERC2016][JZOJ5000]Bipartite Blanket 结论

吕淮晨
2023-12-01

状态压缩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;
}
 类似资料: