jzoj5824 party (Hall定理,最大流转换为最小割DP求解)

版权声明:未经作者本人允许禁止转载。 https://blog.csdn.net/jokerwyt/article/details/81748342

题意

这里写图片描述
n<=3e5,q<=5e4,m<=1e3

模型转换

当确定每个人能带的颜色(特产)后,就变成了一个网络流问题。
每个人向它的颜色连边,颜色向T连1容量的边。
可以二分一个答案mid,然后从S给每个人连这么多容量的边,若能跑满则存在解。因为左边5个点,可以枚举左边怎么选,求出最小割(=最大流)就行。

然而并不需要这么麻烦,容易发现上面的模型可以变为:
将一个人拆成mid个点,判断是否存在对于左侧的完备匹配。
百度一下Hall定理,发现他是存在完备匹配的充要条件
那么,2^5枚举左边如何选择集合,假如左边x个点,右边相连有y个点,那么显然每个人不能拆为超过 y / x 个点。否则就不符合这组限制。

至于如何获取集合,bitset+树剖,再预处理出到链顶的前缀和。
就可以 O ( n l o g n m / w ) 的做了。

另外有bfs就可以求dfs序的黑科技,简直棒棒哒(妈妈再也不怕我爆栈了)

实现

#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#define lowbit(x) ((x) & -(x))
using namespace std;
const int N = 3e5+10;

int n,m,q,fa[N],final[N],nex[N],to[N],tot,a[N],ww[N];
int d[N],g[N][20],top[N],size[N],hvy[N],dfn[N];
bitset<1001> bs[N],t[N*4];

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

int p[6], ans, c;
int l;

void init() {
    for (int i = 1; i <= n; i++) for (int j = 1; j <= 18; j++)
        g[i][j] = g[g[i][j-1]][j-1];
    for (int x = n; x; x--) {
        for (int i = final[x]; i; i=nex[i]) {
            size[x] += size[to[i]];
            if (size[to[i]] > size[hvy[x]]) hvy[x] = to[i];
        }
        size[x]++;
    }
    dfn[1] = 1;
    for (int x = 1; x <= n; x++) {
        if (top[x]==0) top[x] = x;
        else bs[x] |= bs[fa[x]];
        bs[x][a[x]] = 1;

        d[x] = d[fa[x]] + 1;

        int t = dfn[x] + 1;
        if (hvy[x]) {
            top[hvy[x]] = top[x];
            dfn[hvy[x]] = t;
            t += size[hvy[x]];
        }
        for (int i = final[x]; i; i=nex[i]) {
            int y = to[i];
            if (y == hvy[x]) continue;
            dfn[y] = t; t+=size[y];
        }
    }
    for (int i = 1; i <= n; i++) ww[dfn[i]] = i;
}

//change to top
int lca(int x,int y) {
    while (top[x] != top[y]) {
        if (d[top[x]] < d[top[y]]) swap(x,y);
        x = fa[top[x]];
    }
    return d[x] > d[y] ? y : x;
}

void build(int x,int l,int r) {
    if (l == r) {
        t[x][a[ww[l]]] = 1;
        return;
    }
    build(x*2,l,l+r>>1); build(x*2+1,(l+r>>1)+1,r);
    t[x] = t[x*2] | t[x*2+1];
}

int Tl,Tr,Tg;
bitset<1001> bp[6];

void query(int x,int l,int r) {
    if (r<Tl || l > Tr) return;
    if (Tl<= l && r <= Tr) {
        bp[Tg] |= t[x];
        return;
    }
    query(x*2,l,l+r>>1);
    query(x*2+1,(l+r>>1)+1,r);
}

void getbitset(int e) {
    int x = p[e];
    while (top[x] != top[l]) {
        bp[e] |= bs[x];
        x = fa[top[x]];
    }
    Tl = dfn[l], Tr = dfn[x], Tg = e;
    query(1,1,n);
}

int gs[100],id[100];
bitset<1001> dt[32];

int main() {
    freopen("party.in","r",stdin);
    freopen("party.out","w",stdout);
    cin>>n>>m>>q; if (q == 0) return 0;
    for (int i = 2; i <= n; i++) {
        scanf("%d",&fa[i]); g[i][0] = fa[i];
        link(fa[i], i);
    }
    for (int i = 1; i < 32; i++) gs[i] = gs[i - lowbit(i)] + 1;
    for (int i = 1; i < 6; i++) id[1<<i-1] = i;
    for (int i = 1; i <= n; i++) scanf("%d",&a[i]);
    init(); build(1,1,n);
    for (int z = 1; z <= q; z++) {
        scanf("%d",&c);
        for (int i = 1; i <= c; i++) scanf("%d",&p[i]), bp[i].reset();
        l = p[1]; for (int i = 1; i <= c; i++) l = lca(l, p[i]);
        for (int i = 1; i <= c; i++) 
            getbitset(i);
        int ans = 1e8;
        for (int i = 1; i < (1<<c); i++) {
            dt[i] = dt[i - lowbit(i)] | bp[id[lowbit(i)]];
            int z = dt[i].count();
            ans = min(ans, z / gs[i]);
        }
        printf("%d\n",ans * c);
    }
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/81748342