[bzoj3572][Hnoi2014]世界树

来自FallDream的博客,未经允许,请勿转载,谢谢。


世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所卧a与c之间的距离为2。
出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。

n,m<=200000

学习了一下虚树233

比如这道题,每次询问的点不多,但是询问很多,如果每次都在原树上做的话显然是不行的,所以考虑建一棵虚树,只保留要询问的点。

然后对于虚树上的每一个点求离它最近的议事处(其实只要求多加进去的lca的就行了)。

假设size[x]表示x的子树和,对于虚树上的没一条边x->y,找到同时是x的儿子,y的父亲的点t,如果它们的议事处相同,那么这个议事处的答案加上size[t]-size[y],也就是t的子树中y的那部分另算,而剩下的肯定是归属于这个议事处了。

如果不同,那么倍增找到一个分界点,t的子树中除去y的子树,分界点之上的归x,之下的归y。也就是说如果这个分界点叫p,那么ans[bel[x]]+=size[t]-size[p] , ans[bel[y]]+=size[p]-size[y]

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MN 300000
#define MD 18 
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,m,cnt=0,dn=0,dfn[MN+5],head[MN+5],fa[MD+1][MN+5],size[MN+5],dep[MN+5],a[MN+5],b[MN+5];
int bel[MN+5],ans[MN+5],top=0,q[MN+5],c[MN+5];
struct edge{int to,next;}e[MN*2+5];
void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;
}

void init(int x,int f)
{
    fa[0][x]=f;size[x]=1;dfn[x]=++dn;
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=f)
        {
            dep[e[i].to]=dep[x]+1;
            init(e[i].to,x);
            size[x]+=size[e[i].to];
        }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int k=dep[x]-dep[y],j=0;k;k>>=1,j++)
        if(k&1) x=fa[j][x];
    if(x==y) return x;
    for(int i=MD;i>=0;i--)
        if(fa[i][x]!=fa[i][y])
            x=fa[i][x],y=fa[i][y];
    return fa[0][x];
}

bool cmp(int x,int y){return dfn[x]<dfn[y];}
void build()
{
    sort(a+1,a+m+1,cmp);cnt=dn=top=0;
    if(bel[1]!=1) q[++top]=1;
    for(int i=1;i<=m;i++)
    {
        if(top) 
        {
            int x=lca(q[top],a[i]);
            if(x==q[top]) q[++top]=a[i];
            else
            {
                while(top>1&&dep[q[top-1]]>=dep[x])
                    --top,ins(q[top],q[top+1]);
                if(top&&q[top]!=x&&dep[q[top-1]]<dep[x]) 
                    ins(x,q[top]),q[top]=x;
                q[++top]=a[i];
            } 
        }
        else q[++top]=a[i];
    }
    for(;top>1;--top) ins(q[top-1],q[top]);
}

int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}

void dfs(int x,int f)
{
    b[++dn]=x;
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=f)
        {
            dfs(e[i].to,x);
            if(bel[e[i].to])
            {
                if(!bel[x]) bel[x]=bel[e[i].to];else
                {
                    int d1=dis(bel[x],x),d2=dis(bel[e[i].to],x);
                    if(d1>d2||(d1==d2&&bel[e[i].to]<bel[x]))
                         bel[x]=bel[e[i].to];
                }
            }
        }    
}

void dfs2(int x,int f)
{
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=f)
        {
            if(!bel[e[i].to])
                bel[e[i].to]=bel[x];
            else
            {
                int d1=dis(bel[x],e[i].to),d2=dis(bel[e[i].to],e[i].to);
                if(d1<d2||(d1==d2&&bel[x]<bel[e[i].to])) bel[e[i].to]=bel[x];
            }
            dfs2(e[i].to,x);
        }
}

void solve(int x,int y)
{
    int t=y;
    for(int i=MD;i>=0;i--)
        if(dep[fa[i][t]]>dep[x]) t=fa[i][t];
    ans[bel[x]]-=size[t];
    if(bel[x]==bel[y]) ans[bel[x]]+=size[t]-size[y];
    else
    {
        int mid=y;
        for(int i=MD;i>=0;i--)
            if(dep[fa[i][mid]]>dep[x])
            {
                int d1=dis(fa[i][mid],bel[x]),d2=dis(fa[i][mid],bel[y]);
                if(d2<d1||(d2==d1&&bel[y]<bel[x])) mid=fa[i][mid];
            }
        ans[bel[x]]+=size[t]-size[mid];
        ans[bel[y]]+=size[mid]-size[y];
    }
}
int main()
{
    n=read();
    for(int i=1;i<n;i++) ins(read(),read());
    init(1,0);memset(head,0,sizeof(head));
    for(int j=1;j<=MD;j++)
        for(int i=1;i<=n;i++)
            fa[j][i]=fa[j-1][fa[j-1][i]];
    for(int T=read();T;--T)
    {
        m=read();
        for(int i=1;i<=m;i++)c[i]=a[i]=read(),bel[a[i]]=a[i];
        build();dfs(1,0);dfs2(1,0);
        for(int i=1;i<=dn;i++) ans[bel[b[i]]]+=size[b[i]];
        for(int i=1;i<=dn;i++)
            for(int j=head[b[i]];j;j=e[j].next)
                if(j&1) solve(b[i],e[j].to);
        for(int i=1;i<=m;i++) 
            printf("%d ",ans[c[i]]),ans[c[i]]=0;puts("");
        for(int i=1;i<=dn;i++)
            bel[b[i]]=head[b[i]]=0;
    }
    return 0;
}

 

posted @ 2017-04-17 21:37  FallDream  阅读(187)  评论(0编辑  收藏  举报