BZOJ3572: [Hnoi2014]世界树(虚树)

传送门

题意:
一棵树,每次给k个控制点,求每个控制点能控制几个点。(一个点被离它最近的控制点控制)。

题解:
虚树。
首先建出虚树,考虑怎么dp。

首先要dp出离每个虚树上的点最近的点。
dp两次即可。一次找子树,一次找父亲。

其次,对于虚树的根节点,根节点以外的其他所有不在虚树中的点被离虚树中的根节点最近的点控制。

对于虚树中的任意一条路径,倍增求出原树种这条路径上离现在的点最近的点。如果这条路径两边从属一样,那么直接加上即可。

如果不一样,需要倍增查找分界点(一半从属下面,一半从属上面)。分别累加。

对于不在虚树中的子树,大小为该点的子树大小减去虚树中最近点的子树大小。

  • Code
#include<bits/stdc++.h>
using namespace std;
namespace IO
{
    streambuf *ib,*ob;int buf[50];
    inline void init()
    {
        ios::sync_with_stdio(false);
        cin.tie(NULL);cout.tie(NULL);
        ib=cin.rdbuf();ob=cout.rdbuf();
    }
    inline int read()
    {
        char ch=ib->sbumpc();int i=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}
        while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}
        return i*f;
    }
    inline void W(int x)
    {
        if(!x){ob->sputc('0');return;}
        if(x<0){ob->sputc('-');x=-x;}
        while(x){buf[++buf[0]]=x%10;x/=10;}
        while(buf[0])ob->sputc(buf[buf[0]--]+'0');
    }
}

const int Maxn=3e5+50;
int n,m,rt,q,vt,dfn[Maxn],ind,dep[Maxn],sze[Maxn],fa[Maxn][20],vir[Maxn],id[Maxn],dis[Maxn],belong[Maxn],ans[Maxn];
vector<int>edge[Maxn];
vector<int>nowedge[Maxn];
inline int getlca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    if(dep[x]!=dep[y])
    {
        int b=dep[x]-dep[y];
        for(int i=0;i<=18&&b;i++)
        {
            if(b&1)x=fa[x][i];
            b>>=1;
        }
    }
    if(x==y)return x;
    for(int i=18;i>=0;i--)
    {
        if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    }
    return fa[x][0];
}
inline void dfs(int now,int f)
{
    dep[now]=dep[f]+1;sze[now]=1;fa[now][0]=f;dfn[now]=++ind;
    for(int i=1;i<=18;i++)fa[now][i]=fa[fa[now][i-1]][i-1];
    for(int e=edge[now].size()-1;e>=0;e--)
    {
        int v=edge[now][e];
        if(v==f)continue;
        dfs(v,now);sze[now]+=sze[v];
    }
}
inline bool compdfn(const int &a,const int &b){return  dfn[a]<dfn[b];}
inline void BuildAuxTree()
{
    static int a[Maxn],nowfa[Maxn],cnt,sta[Maxn],top;
    cnt=m=IO::read();++vt;top=0;
    for(int i=1;i<=m;i++)a[i]=vir[i]=IO::read(),id[vir[i]]=vt,ans[a[i]]=0;
    sort(a+1,a+m+1,compdfn);
    for(int i=1;i<=m;i++)
    {
        if(!top)
        {
            rt=a[i];
            nowfa[a[i]]=0;
            sta[++top]=a[i];
            continue;
        }
        int lca=getlca(a[i],sta[top]);
        if(lca!=sta[top])
        {
            while(dep[sta[top]]>dep[lca])
            {
                if(dep[sta[top-1]]<dep[lca])nowfa[sta[top]]=lca;
                top--;
            }
            if(lca!=sta[top])
            {
                if(!top)rt=lca;
                a[++cnt]=lca;
                nowfa[lca]=sta[top];
                sta[++top]=lca;
            }
        }
        nowfa[a[i]]=sta[top];
        sta[++top]=a[i];
    }
    for(int i=1;i<=cnt;i++)nowedge[a[i]].clear();
    for(int i=1;i<=cnt;i++)nowedge[nowfa[a[i]]].push_back(a[i]);
}
inline void FindClose1(int now,int f)
{
    if(id[now]==vt)dis[now]=0,belong[now]=now;
    else dis[now]=n+1,belong[now]=n+1;
    for(int e=nowedge[now].size()-1;e>=0;e--)
    {
        int v=nowedge[now][e];
        if(v==f)continue;
        FindClose1(v,now);
        int len=dep[v]-dep[now];
        if(dis[now]>dis[v]+len)dis[now]=dis[v]+len,belong[now]=belong[v];
        else if(dis[now]==dis[v]+len&&belong[now]>belong[v])belong[now]=belong[v];
    }
}
inline void FindClose2(int now,int f,int dist)
{
    if(now!=rt)
    {
        if(dis[f]+dist<dis[now])dis[now]=dis[f]+dist,belong[now]=belong[f];
        else if(dis[f]+dist==dis[now]&&belong[now]>belong[f])belong[now]=belong[f];
    }
    for(int e=nowedge[now].size()-1;e>=0;e--)
    {
        int v=nowedge[now][e];
        if(v==f)continue;
        FindClose2(v,now,dep[v]-dep[now]);
    }
}
inline int GetClosest(int x,int y)
{
    for(int i=18;i>=0;i--)
        if(dep[fa[x][i]]>dep[y])x=fa[x][i];
    return x;
}
inline void GetAns(int now,int f)
{
    if(now==rt)ans[belong[now]]+=sze[1]-sze[now];
    int w=sze[now];
    for(int e=nowedge[now].size()-1;e>=0;e--)
    {
        int v=nowedge[now][e];
        if(v==f)continue;
        int suf=GetClosest(v,now);
        w-=sze[suf];
        if(belong[v]==belong[now])ans[belong[now]]+=(sze[suf]-sze[v]);
        else
        {
            int suf2=v;
            for(int i=18;i>=0;i--)
            {
                if(dep[fa[suf2][i]]>dep[now])
                {
                    if(dep[v]-dep[fa[suf2][i]]+dis[v]<dep[fa[suf2][i]]-dep[now]+dis[now])suf2=fa[suf2][i];
                    else if((dep[v]-dep[fa[suf2][i]]+dis[v]==dep[fa[suf2][i]]-dep[now]+dis[now])&&(belong[v]<belong[now]))suf2=fa[suf2][i];
                }
            }
            ans[belong[now]]+=sze[suf]-sze[suf2];
            ans[belong[v]]+=sze[suf2]-sze[v];
        }
        GetAns(v,now);
    }
    ans[belong[now]]+=w;
}
int main()
{
    IO::init();n=IO::read();
    for(int i=1;i<n;i++)
    {
        int x=IO::read(),y=IO::read();
        edge[x].push_back(y);edge[y].push_back(x);
    }
    dfs(1,0);
    q=IO::read();
    while(q--)
    {
        BuildAuxTree();
        FindClose1(rt,0);
        FindClose2(rt,0,0);
        GetAns(rt,0);
        for(int i=1;i<=m;i++)IO::W(ans[vir[i]]),IO::ob->sputc(' ');
        IO::ob->sputc('\n');
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值