[bzoj5404]party——树链剖分+Hall定理 大佬们的博客 Some Links

题面:

这里写图片描述

思路:

好像虽然思维难度不大但是考试的时候还是没有很多的人A。
首先我们可以发现既然要所有的人都要尽快到达的话那肯定是在lca的地方聚会。
然后题意就可以简化为每一个人都到lca并且只可以选择lca上的特产,所以我们要处理一条链上的颜色种数。
然后我就不会处理了,后来问了一下学长发现竟然使用bitset来维护,因为注意到颜色总共的种类只开到了1000,所以用个线段树+bitset来维护应该是没有问题的,如果怕这样会卡时间的话可以预处理每一条链上的颜色的集合的bitset前缀和,然后跳链的时候就没有必要再用一次线段树了,只是后面最后的一条链还要用一次线段树。
然后我们来考虑怎么来求最大的特产数,注意到每一个人选择的特产的种数是要相同的,假设每一个人选择的特产是 x 种,那么答案就是 c x ,相当于每一个人都拆成了 x 点,然后人和特产之间连边跑二分图匹配,但是要求必须是完美匹配,这个时候Hall定理就要派上用场了,左边的任何一个子集都要满足和右边的相邻的点数大于子集大小,此时 x 的意义就相当是把左边的点集复制了 x 遍,所以答案很明显就是 min ( ϝ ( S ) S ) 了。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("20180624T2_modify.in","r",stdin);
    freopen("20180624T2_modify.out","w",stdout);
}

template<typename T>
void read(T &x){
    T _=0,mul=1;
    char __=getchar();
    while(!isdigit(__)){
        if(__=='-')mul=-1;
        __=getchar();
    }
    while(isdigit(__))_=(_<<1)+(_<<3)+(__^'0'),__=getchar();
    x=_*mul;
}

template<typename T>
void write(T x,char c){
    if(x==0){
        putchar('0');
        putchar(c);
        return;
    }
    if(x<0){putchar('-');x=-x;}
    T len=1,y=10;
    while(y<=x)y=(y<<3)+(y<<1),++len;
    while(len--){
        y/=10;
        putchar(x/y+48);
        x%=y;
    }
    putchar(c);
}

const int inf=0x3f3f3f3f;
const int maxn=3e5+10;
const int maxm=1000+10;
int n,m,q,to[maxn],las[maxn],beg[maxn],cnte;
int fa[maxn],son[maxn],dep[maxn],siz[maxn],top[maxn],dfn[maxn],cnt_dfn;
void add(int u,int v){las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;}
int a[maxn],co[maxn];
bitset<maxm>col[maxn<<2],sum[2][maxn];

void dfs1(int u){
    siz[u]=1;
    dep[u]=dep[fa[u]]+1;
    for(int i=beg[u];i;i=las[i]){
        dfs1(to[i]);
        siz[u]+=siz[to[i]];
        if(siz[to[i]]>siz[son[u]])
            son[u]=to[i];
    }
}

void dfs2(int u,int t){
    sum[0][u][a[u]]=sum[1][u][a[u]]=1;
    if(u!=t)sum[0][u]|=sum[0][fa[u]];
    top[u]=t;
    dfn[u]=++cnt_dfn;
    if(son[u]){
        dfs2(son[u],t);
        sum[1][u]|=sum[1][son[u]];
    }
    for(int i=beg[u];i;i=las[i]){
        if(to[i]==son[u])continue;
        dfs2(to[i],to[i]);
    }
}

struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc rt<<1
#define rc rt<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
    void build(int rt,int l,int r){
        if(l==r){col[rt][co[l]]=1; return;}
        build(lson); build(rson);
        col[rt]=col[lc]|col[rc];
    }
    bitset<maxm> query(int rt,int l,int r,int L,int R){
        if(L<=l && r<=R)return col[rt];
        if(L<=mid && R>=mid+1)return query(lson,L,R)|query(rson,L,R);
        if(L<=mid)return query(lson,L,R);
        else return query(rson,L,R);
    }
}T;

void init(){
    read(n); read(m); read(q);
    REP(i,2,n){
        read(fa[i]);
        add(fa[i],i);
    }
    REP(i,1,n)read(a[i]);
    dfs1(1);
    dfs2(1,1);
    REP(i,1,n)co[dfn[i]]=a[i];
    T.build(1,1,n);
}

int get_lca(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v] ? u : v;
}

bitset<maxm>get_S(int u,int an){
    bitset<maxm>ret;
    while(top[u]!=top[an]){
        ret|=sum[0][u];
        u=fa[top[u]];
    }
    ret|=T.query(1,1,n,dfn[an],dfn[u]);
    return ret;
}

void solve(){
    int c,lca,b[6],ans=inf;
    bitset<maxm>S[6];
    read(c);
    REP(i,1,c)read(b[i]);
    lca=get_lca(b[1],b[2]);
    REP(i,3,c)lca=get_lca(lca,b[i]);
    REP(i,1,c)S[i]=get_S(b[i],lca);
    REP(S0,1,(1<<c)-1){
        bitset<maxm>t;
        REP(i,1,c)if((1<<(i-1))&S0)
            t|=S[i];
        ans=min(ans,(int)(t.count()/__builtin_popcount(S0)));
    }
    write(ans*c,'\n');
}

int main(){
    File();
    init();
    REP(i,1,q)solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80845992