P3679 [CERC2016] 二分毯 Bipartite Blanket

· · 题解

考虑霍尔定理和广义霍尔定理:

霍尔定理:对于一个左部图为 X、右部图大小为 Y 的二分图(钦定 |X|\leq |Y|),存在边数等于 |X| 的匹配的充要条件是:对于左部图的任何一个点集,右部图中和它相邻的点集大小都大于等于它(相邻的点集指的是所有点出边的并集)。

广义霍尔定理:给定一张二分图。如果存在一个匹配覆盖左部图中的点集 X,且存在一个匹配覆盖右部图中的点集 Y,那么存在一个匹配同时覆盖 XY

根据广义霍尔定理,我们对于点集 A 与点集 B 单独考虑合法性,然后再合并方案即可。

时间复杂度 O(n2^n+m2^m)

#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
const int N = 22, M = (1 << 20);
int n, m, U, t, t1, t2, a[N], b[N]; ll ans;
int e1[M], e2[M], r1[M], r2[M], w1[M], w2[M], c1[M], c2[M];
void check(int a[], int e[], int w[], int c[]){
    FL(s, 0, U){
        FL(i, 1, n) if(s & (1 << i - 1))
            w[s] += a[i], c[s] |= e[i];
        c[s] = (__builtin_popcount(c[s]) >= __builtin_popcount(s));
    }
    FL(i, 1, n) FL(s, 0, U) if(s & (1 << i - 1))
        c[s] = min(c[s], c[s ^ (1 << i - 1)]);
}
int main(){
    scanf("%d%d", &n, &m);
    FL(i, 1, n) FL(j, 1, m){
        char ch; scanf(" %c", &ch);
        e1[i] |= (ch - 48 << j - 1);
        e2[j] |= (ch - 48 << i - 1);
    }
    FL(i, 1, n) scanf("%d", &a[i]);
    FL(i, 1, m) scanf("%d", &b[i]);
    scanf("%d", &t), U = (1 << (n = max(n, m))) - 1;
    check(a, e1, w1, c1), check(b, e2, w2, c2);
    FL(s, 0, U){
        if(c1[s]) r1[++t1] = w1[s];
        if(c2[s]) r2[++t2] = w2[s];
    }
    sort(r1 + 1, r1 + t1 + 1);
    sort(r2 + 1, r2 + t2 + 1);
    int j = t2 + 1;
    FL(i, 1, t1){
        while(j > 1 && r2[j - 1] + r1[i] >= t) j--;
        ans += t2 - j + 1;
    }
    printf("%lld\n", ans);
    return 0;
}