party(最小割优化最大流或霍尔定理)

Description:

这里写图片描述

题解:

很容易想到一个做法就是:
1.先求出c个点的lca
2.接着求出每个点到lca的路径上经过的有哪些特产
3.然后二分答案,建图网络流

第三步可以用动态加边网络流优化掉那个log。

第二步可以用树链剖分+线段树优化,预处理每个点到其重链链顶的答案,用bitset优化,即可做到:
时间复杂度 O ( n   l o g   n m / 32 ) ,空间复杂度约为 O ( 4 n   l o g   n m / 32 )

但是第三步还是跑不过,这是个二分图,根据最大流=最小割,可以优化。

注意到左边的点数=c<=5,所以用 2 c 暴力枚举左边断哪些边,bitset随便or一下剩下的点即可得出右边断哪些边,根据题目限制,即可得出左边每条边的边权,不用二分答案。

设左边每条边的边权为mid,左边断了l条边,右边断了r条边,则此割大小为 l m i d + r
此割要合法,则
l m i d + r >= c m i d
m i d <= r c l

因为是最小割,取 r c l 的min值即可。

还有一种理解是霍尔定理。

霍尔定理为:
一个二分图存在完美匹配的充要条件是对于左边点集的任意一个子集S,S的出边指向右边不小于 | S | 个点。

考虑一个边权mid是否会存在完美匹配,则左边的点的所有子集的出边需要指向不小于它的子集大小的点,注意霍尔定理必须是二分图,若S到x的边权为mid,把x拆成mid个来想象。

然后推一下是一样的。

Code:

#include<cstdio>
#include<bitset>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define i0 i + i
#define i1 i + i + 1
using namespace std;

const int N = 3e5 + 5;

int n, m, q, c, h, b[6], fa[N], a[N], dep[N];
int final[N], to[N], next[N], tot;
int siz[N], son[N], top[N], tw, w[N], nw[N], ans;

void link(int x, int y) {
    next[++ tot] = final[x], to[tot] = y, final[x] = tot;
}

void dg(int x) {
    siz[x] = 1;
    for(int i = final[x]; i; i = next[i])
        dg(to[i]), siz[x] += siz[to[i]], son[x] = siz[to[i]] > siz[son[x]] ? to[i] : son[x];
}

void dfs(int x) {
    w[x] = ++ tw;
    if(son[x]) top[son[x]] = top[x], dfs(son[x]);
    for(int i = final[x]; i; i = next[i]) if(to[i] != son[x])
        top[to[i]] = to[i], dfs(to[i]);
}

bitset<1001> t[N * 4];

void bt(int i, int x, int y) {
    if(x == y) {t[i][a[nw[x]]] = 1; return;}
    int m = x + y >> 1;
    bt(i0, x, m); bt(i1, m + 1, y);
    t[i] = t[i0] | t[i1];
}
int pl, pr; bitset<1001> px;
void fi(int i, int x, int y) {
    if(y < pl || x > pr) return;
    if(x >= pl && y <= pr) {px |= t[i]; return;}
    int m = x + y >> 1;
    fi(i0, x, m); fi(i1, m + 1, y);
}
bitset<1001> tp[N];

int lca(int x, int y) {
    while(top[x] != top[y]) if(dep[top[x]] >= dep[top[y]])
        x = fa[top[x]]; else y = fa[top[y]];
    return dep[x] < dep[y] ? x : y;
}

bitset<1001> f[6];

int bx[6];

void dd(int x) {
    if(x > c) {
        int ps = 0; px.reset();
        fo(i, 1, c) if(bx[i])
            px |= f[i]; else ps ++;
        if(ps == c) return;
        int mi = px.count() / (c - ps);
        ans = min(ans, mi);
        return;
    }
    bx[x] = 1; dd(x + 1); bx[x] = 0; dd(x + 1);
}

int main() {
    freopen("party.in", "r", stdin);
    freopen("party.out", "w", stdout);
    scanf("%d %d %d", &n, &m, &q);
    dep[1] = 1;
    fo(i, 2, n) scanf("%d", &fa[i]), dep[i] = dep[fa[i]] + 1;
    fo(i, 1, n) scanf("%d", &a[i]);
    fo(i, 2, n) link(fa[i], i);
    dg(1); top[1] = 1; dfs(1);
    fo(i, 1, n) nw[w[i]] = i;
    bt(1, 1, n);
    fo(i, 1, n) {
        px.reset(); pl = w[top[i]]; pr = w[i];
        fi(1, 1, n); tp[i] = px;
    }
    fo(ii, 1, q) {
        scanf("%d", &c); int s = 1e9;
        fo(i, 1, c) scanf("%d", &b[i]);
        h = b[1]; fo(i, 2, c) h = lca(h, b[i]);
        fo(i, 1, c) {
            int x = b[i]; f[i].reset();
            for(; top[x] != top[h]; x = fa[top[x]])
                f[i] |= tp[x];
            px.reset(); pl = w[h]; pr = w[x];
            fi(1, 1, n); f[i] |= px;
        }
        ans = 1e9;
        dd(1);
        if(ans == 1e9) printf("0\n"); else printf("%d\n", ans * c);
    }
}

猜你喜欢

转载自blog.csdn.net/Cold_Chair/article/details/81747074