bzoj 5404: party 树链剖分+hall定理

Description
这里写图片描述
Input
这里写图片描述
Output
这里写图片描述
Sample Input

5 3 4
1 2 2 1
2 3 1 3 1
2 3 4
3 5 2 2
4 3 4 2 5
2 2 2

Sample Output

2
3
0
0

Data Constraint
这里写图片描述

分析:
因为树上的边单向,显然选择在 lca l c a 处是最优的。
对于特产的类别,我们考虑用一个 bitset b i t s e t 来维护,因为 n n 比较大,所以只能选择线段树+树剖来算链上的特产,就不能用倍增了。
每个人带的特产数的最大值x,相当于把每个人拆成 x x 个点,与bitset对应的 1 1 位置连边,跑最大匹配,而且一定是完全匹配,这个可以直接用hall定理来求。
hall定理:
X点集大小为 n n Y点集大小为 m m n<m,对于 i[1,n] ∀ i ∈ [ 1 , n ] 大小为 i i X点集的子集,都至少与 Y Y i个点直接相连,满足则有完全匹配。

代码:

/**************************************************************
    Problem: 5404
    User: liangzihao
    Language: C++
    Result: Accepted
    Time:9104 ms
    Memory:201640 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <cmath>
#include <bitset>

const int maxn=3e5+7;

using namespace std;

int n,m,test,x,cnt,ans;
int f[maxn][20],dep[maxn],size[maxn],top[maxn],dfn[maxn],ls[maxn];
int a[10];

struct edge{
    int y,next;
}g[maxn];

bitset <1001> t[maxn*4],bit[10],h[107];

void dfs1(int x,int fa)
{
    f[x][0]=fa;
    size[x]=1;
    dep[x]=dep[fa]+1;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (g[i].y==fa) continue;
        dfs1(y,x);
        size[x]+=size[y];
    }
}

void dfs2(int x,int d)
{
    dfn[x]=++cnt;
    top[x]=d;
    int c=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (y==f[x][0]) continue;
        if (size[y]>size[c]) c=y;
    }
    if (!c) return;
    dfs2(c,d);
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((y==f[x][0]) || (y==c)) continue;
        dfs2(y,y);
    }
}

void ins(int p,int l,int r,int x,int k)
{
    if (l==r)
    {
        t[p][k]=1;
        return;
    }
    int mid=(l+r)/2;
    if (x<=mid) ins(p*2,l,mid,x,k);
           else ins(p*2+1,mid+1,r,x,k);
    t[p]=t[p*2]|t[p*2+1];
}

int lca(int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    int k=19,t=1<<k,d=dep[y]-dep[x];
    while (d)
    {
        if (d>=t) d-=t,y=f[y][k];
        t/=2; k--;
    }
    if (x==y) return x;
    k=19;
    while (k>=0)
    {
        if (f[x][k]!=f[y][k])
        {
            x=f[x][k];
            y=f[y][k];
        }
        k--;
    }
    return f[x][0];
}

void getans(int p,int l,int r,int x,int y,int k)
{
    if ((l==x) && (r==y))
    {
        bit[k]|=t[p];
        return;
    }
    int mid=(l+r)/2;
    if (y<=mid) getans(p*2,l,mid,x,y,k);
    else if (x>mid) getans(p*2+1,mid+1,r,x,y,k);
    else
    {
        getans(p*2,l,mid,x,mid,k);
        getans(p*2+1,mid+1,r,mid+1,y,k);
    }
}

void get(int x,int y,int k)
{
    bit[k].reset();
    while (top[x]!=top[y])
    {
        getans(1,1,n,dfn[top[y]],dfn[y],k);
        y=f[top[y]][0];
    }
    getans(1,1,n,dfn[x],dfn[y],k);
}

int calc(int x)
{
    int sum=0;
    for (int i=x;i;i-=i&(-i)) sum++;
    return sum;
}

int main()
{
    scanf("%d%d%d",&n,&m,&test);
    for (int i=2;i<=n;i++)
    {
        scanf("%d",&x);
        g[i].y=i;
        g[i].next=ls[x];
        ls[x]=i;
    }
    dfs1(1,0);
    dfs2(1,0);
    for (int j=1;j<20;j++)
    {
        for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        ins(1,1,n,dfn[i],x);
    }   
    for (int i=1;i<=test;i++)
    {
        scanf("%d",&x); 
        for (int j=1;j<=x;j++) scanf("%d",&a[j]);
        int d=a[1];
        for (int j=2;j<=x;j++) d=lca(d,a[j]);
        for (int j=1;j<=x;j++) get(d,a[j],j);
        for (int i=0;i<(1<<x);i++) h[i].reset();
        ans=0x3f3f3f3f;
        for (int i=1;i<(1<<x);i++)
        {
            int s=i&(-i);
            int k=trunc(log(s+0.5)/log(2));
            h[i]=h[i-s]|bit[(k+1)];
            ans=min(ans,(int)h[i].count()/calc(i));
        }
        printf("%d\n",ans*x);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值