JZOJ5824.party

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/XLno_name/article/details/81747107

题意:

数据范围:

Analysis

首先考虑办聚会的城市,由于道路是单向的,不难发现该城市就是这 c 个城市的 l c a .
考虑选择礼品的限制如何解决,思考一下发现是对于每一个礼品分配城市,考虑网络流解决.
至于所有城市的礼品数一样我们可以二分限制最大流,判断是否能满流即可。
我考场上考虑到这里,事实上已经能拿到 65 分了,本来想能不能最大流转最小割做 d p ,发现并不行。
运用 H a l l 定理,每一次二分,可以相当于把每个城市拆成 m i d 个点,两边二分图匹配,只要知道是否有完备匹配即可,那么根据 H a l l 定理,两边有完备匹配的必要条件是:
左部任意一个子集相邻的点都 >= 子集大小。
那么每一个子集都找出它最多能够给每个城市分配多少礼品,即右方相邻点数目除以子集大小。
至于每一个点到聚会城市的颜色数目,颜色种类只有 1000 ,直接线段树维护一下 b i t s e t ,用树链剖分解决,预处理每一个点到链顶的 b i t s e t ,复杂度可以做到 O ( q c l o g n 1000 w )

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
# include<bitset>
using namespace std;
const int N = 3e5 + 5;
const int M = 1e3 + 5;
bitset <M> c[N << 2],s[N],a[15],las;
int in[N],top[N],fa[N],dep[N],siz[N],son[N],id[N];
int v[N],to[N],nx[N],st[N],z[15];
int n,m,tot,q;
inline int read()
{
    int x = 0; char ch = getchar();
    for (; ch < '0' || ch > '9' ; ch = getchar());
    for (; ch >= '0' && ch <= '9' ; ch = getchar()) x = x * 10 + ch - '0';
    return x;
}
void add(int u,int v) { to[++tot] = v,nx[tot] = st[u],st[u] = tot; }
inline void build(int x,int l,int r)
{
    if (l == r) { c[x][v[id[l]]] = 1; return; }
    int mid = (l + r) >> 1;
    build(x << 1,l,mid); build(x << 1 | 1,mid + 1,r);
    c[x] = c[x << 1] | c[x << 1 | 1];
}
inline void qry(int x,int l,int r,int l1,int r1,int p)
{
    if (l >= l1 && r <= r1) { a[p] |= c[x]; return; }
    int mid = (l + r) >> 1;
    if (l1 <= mid) qry(x << 1,l,mid,l1,r1,p);
    if (r1 > mid) qry(x << 1 | 1,mid + 1,r,l1,r1,p);
}
void dfs(int x)
{
    siz[x] = 1;
    for (int i = st[x] ; i ; i = nx[i])
    {
        dfs(to[i]),siz[x] += siz[to[i]];
        if (siz[to[i]] > siz[son[x]]) son[x] = to[i];
    }
}
void dfs1(int x,int f,int t)
{
    in[x] = ++tot,id[tot] = x,top[x] = t;
    if (x != t) s[x] = s[f]; s[x][v[x]] = 1;
    if (son[x]) dfs1(son[x],x,t);
    for (int i = st[x] ; i ; i = nx[i])
    if (to[i] != son[x]) dfs1(to[i],x,to[i]);
}
inline int find(int x,int y)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x,y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}
inline void solve(int x,int y,int p)
{
    while (top[x] != top[y])
    {
        a[p] |= s[x];
        x = fa[top[x]];
    }
    qry(1,1,n,in[y],in[x],p);
}
int main()
{
    n = read(),m = read(),q = read();
    for (int i = 2 ; i <= n ; ++i) fa[i] = read(),dep[i] = dep[fa[i]] + 1,add(fa[i],i);
    for (int i = 1 ; i <= n ; ++i) v[i] = read();
    tot = 0,dfs(1);
    dfs1(1,0,1); build(1,1,n);
    while (q--)
    {
        int num = read();
        for (int i = 1  ; i <= num ; ++i) z[i] = read(),a[i].reset();
        int lca = z[1];
        for (int i = 2 ; i <= num ; ++i) lca = find(lca,z[i]);
        for (int i = 1 ; i <= num ; ++i) solve(z[i],lca,i);
        int ans = M;
        for (int i = 1 ; i < (1 << num) ; ++i)
        {
            int cnt = 0; las.reset();
            for (int j = 1 ; j <= num ; ++j)
            if (i & (1 << (j - 1))) ++cnt,las |= a[j];
            ans = min(ans,(int)las.count() / cnt);
        }
        printf("%d\n",ans * num);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/XLno_name/article/details/81747107