zoukankan      html  css  js  c++  java
  • [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;
    }
  • 相关阅读:
    BBS登入和数据库迁移部分
    Auth组件
    【作业】返回一个整形数组中最大子数组地和——当维度达到二维/*待完善*/
    【作业】返回一个整形数组中最大子数组地和——当数量达到10亿
    软件工程课程周学习进度报告——第三周
    软件工程课程周学习进度报告——第二周
    软件工程第一周开课博客
    【作业】返回一个整形数组中最大子数组地和
    《人月神话》读后感其三——第二个系统问题
    《人月神话》读后感其二——从未考虑过的多人协作问题
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj3572.html
Copyright © 2011-2022 走看看