[HNOI2014]世界树

题目描述

世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有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年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。

题解

这题快调死我了。。。

还是细节掌握的不到位。。。

前置知识:虚树

就是我们在处理一颗树的时候,它的节点很多,但我们实际用到的点却很少时,就可以用虚树来处理。

其实就是保留了需要用的点和它们的LCA,所以节点数是O(n)的。

建树方法:维护一个栈,一开始只有1节点。

把所有点按照dfs序排序。

然后加入一个点,查一下它和栈顶的LCA,如果答案就是栈顶,那么堆栈。

否则就不停的弹栈,然后弹栈的时候连边,注意最后一次弹栈的时候的连边方式。

然后把LCA和当前点堆栈。

注意,为了防止出现自环,要写上。

1、if(a[i].a==st[top])continue;
2、if(lca!=st[top])

然后这道题就是把虚树建出来。

如果这棵树里的每个点都是关键点就好做了。

但是里面有LCA点,所以我们对每个LCA点维护立它最近的关键点。

这个可以两遍dfs求。

然后找到每一条链,计算这条链的贡献,一条链最多会对两个关键点产生贡献,讨论一下就可以了。

我太zz分了四种情况讨论。。。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 300009
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
int head[N],deep[N],p[N][20],dfn[N],size[N],dp[N],len[N],tot,tot1,h[N],son[N],dfnn,n,q,m,st[N],top,rbs[N],ans[N],ddf_son[N];
bool vis[N];
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct edge{int n,to;}e[N<<1],E[N<<1];
struct node{
    int a,id;    
    inline bool operator <(const node &b)const{return dfn[a]<dfn[b.a];}
}a[N];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
inline void link(int u,int v){E[++tot1].n=h[u];E[tot1].to=v;h[u]=tot1;}
inline int getlca(int a,int b){
    if(deep[a]<deep[b])swap(a,b);
    for(int i=18;i>=0;--i)if(deep[a]-(1<<i)>=deep[b])a=p[a][i];
    if(a==b)return a;
    for(int i=18;i>=0;--i)if(p[a][i]!=p[b][i])a=p[a][i],b=p[b][i];
    return p[a][0];
}
inline int jump(int x,int dep){
    for(int i=18;i>=0;--i)if(dep&(1<<i))x=p[x][i];
    return x;
} 
void dfs(int u,int fa){
    dfn[u]=++dfnn; 
    size[u]=1;
    for(int i=1;(1<<i)<=deep[u];++i)p[u][i]=p[p[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
        int v=e[i].to;p[v][0]=u;deep[v]=deep[u]+1;
        dfs(v,u); size[u]+=size[v];
    }
}
void efs(int u){
    int x=size[u];
    if(vis[u])son[u]=u,len[u]=-1;
    for(int i=h[u];i;i=E[i].n){
        int v=E[i].to;
        efs(v);
        int ha=jump(v,deep[v]-deep[u]-1);    
        x-=size[ha];
        if(!vis[u]){
            if(deep[son[v]]-deep[u]-1<len[u]||(deep[son[v]]-deep[u]-1==len[u]&&son[v]<son[u])){
                son[u]=son[v];len[u]=deep[son[v]]-deep[u]-1;
            }
        }    
    }
    dp[u]=x; 
}
void hfs(int u){
    for(int i=h[u];i;i=E[i].n){
        int v=E[i].to;
        if(!vis[v]){
            if(deep[v]-deep[u]+len[u]<len[v]||(deep[v]-deep[u]+len[u]==len[v]&&son[u]<son[v])){
                son[v]=son[u];len[v]=deep[v]-deep[u]+len[u];
            }
        }    
        hfs(v);
   }
}
void gfs(int u){
    //cout<<u<<" "<<son[u]<<endl;
    if(!vis[u])dp[son[u]]+=dp[u];
    for(int i=h[u];i;i=E[i].n){
        int v=E[i].to;gfs(v);
        int de=jump(v,deep[v]-deep[u]-1);
        if(vis[u]&&vis[v]){
        //    cout<<u<<" "<<v<<endl;
            int gua=deep[v]-deep[u]-1;
            int y=gua>>1;if((gua&1)&&v<u)y++;
            int lca=jump(v,y);
            dp[u]+=size[de]-size[lca];dp[v]+=size[lca]-size[v];
        }
        else if(vis[u]&&!vis[v]){
            if(u==son[v])dp[u]+=size[de]-size[v];
            else{
                int gua=len[v]+deep[v]-deep[u];
                int y=gua>>1;if((gua&1)&&u<son[v])y++;
                int lca=jump(v,deep[v]-deep[u]-y-1); 
                dp[u]+=size[de]-size[lca];dp[son[v]]+=size[lca]-size[v];
            }
        }
        else if(vis[v]&&!vis[u]){
            if(v==son[u])dp[v]+=size[de]-size[v];
            else{
                int gua=len[u]+deep[v]-deep[u];
                int y=gua>>1;if((gua&1)&&v<son[u])y++;
                int lca=jump(v,y); 
                dp[v]+=size[lca]-size[v];dp[son[u]]+=size[de]-size[lca];
            }
        }
        else{
            int gua=deep[v]-deep[u]+1+len[u]+len[v];
            int y=gua>>1;if((gua&1)&&son[v]<son[u])y++;
            if(y<=len[v]+1)dp[son[u]]+=size[de]-size[v];
            else if(y>=len[v]+deep[v]-deep[u])dp[son[v]]+=size[de]-size[v];
            else{
               int lca=jump(v,y-len[v]-1);
               dp[son[u]]+=size[de]-size[lca];dp[son[v]]+=size[lca]-size[v];
            }
        }
    } 
}
int main(){
    n=rd();int u,v;
    for(int i=1;i<n;++i){u=rd();v=rd();add(u,v);add(v,u);} 
    dfs(1,0);
    memset(len,0x3f,sizeof(len));
    q=rd();
    while(q--){
        m=rd();
        for(int i=1;i<=m;++i)a[i].a=rd(),a[i].id=i,vis[a[i].a]=1;
        sort(a+1,a+m+1);
        st[top=1]=1;rbs[++rbs[0]]=1;
        for(int i=1;i<=m;++i){
            if(a[i].a==st[top])continue;
            int lca=getlca(st[top],a[i].a);
            if(lca==st[top]){st[++top]=a[i].a;rbs[++rbs[0]]=a[i].a;continue;}
            while(1){
                int fi=st[top],se=st[top-1];
                if(dfn[lca]>=dfn[se]){
                    link(lca,fi);top--;break;
                }
                else{link(se,fi);top--;}
            }
            if(lca!=st[top]){
                st[++top]=lca;rbs[++rbs[0]]=lca;
            }
            st[++top]=a[i].a;rbs[++rbs[0]]=a[i].a;
        }
        while(top>1)link(st[top-1],st[top]),top--;    
        efs(1);hfs(1);gfs(1);
        for(int i=1;i<=m;++i)ans[a[i].id]=dp[a[i].a];
        for(int i=1;i<=m;++i)printf("%d ",ans[i]);puts("");
        tot1=0;
        while(rbs[0]){
           int x=rbs[rbs[0]];
          vis[x]=0,dp[x]=0,len[x]=inf,son[x]=0,h[x]=0,ddf_son[x]=0;rbs[0]--;
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ZH-comld/p/10299777.html