【洛谷】世界树-虚数/树形DP

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/79952494

传送门:洛谷-世界树


题意

世界树的形态可以用一个数学模型来描述:世界树中有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<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000


题解

先建一棵树模拟一下,就会发现对于每个点,如果它的子树里都没有议事处,那么肯定归它管,对于它上面的,找到距离恰为和祖先中第一个议事处距离的一半的,判一下标号大小就好了。
那么结合m的提示,我们对于每一次询问建一颗虚树。


代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,x,y,h[N],tot,Q,m,dfn,flor;
int head[N],to[N<<1],nxt[N<<1],a[N],b[N],c[N],top;
int d[N],ans[N],f[N][20],sz[N],rem[N];
int id[N],bel[N],s[N],mx; 

inline int read()
{
    char ch=getchar();int x=0,t=1;
    while(ch<'0' || ch>'9') {if(ch=='-') t=-1;ch=getchar();}
    while(ch>='0' && ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*t;
}

inline bool cmp(int x,int y)
{
    return id[x]<id[y];
}

inline int lca(int x,int y)
{
   if(d[x]>d[y]) swap(x,y);
   int t=d[y]-d[x];
   for(register int i=0;(1LL<<i)<=t;i++){
       if(t&(1LL<<i)) y=f[y][i];
   }
   for(register int i=flor;i>=0;i--){
      if(f[x][i]!=f[y][i]){
         x=f[x][i];y=f[y][i];
      }
   }
   if(x!=y){
    return f[x][0];
   }else{
    return x;
   }
}

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

inline void lk(int u,int v)
{
    to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

inline void dfs(int x,int fa)
{
    int e;sz[x]=1;id[x]=++dfn;
    for(register int i=1;(1LL<<i)<=d[x];i++){
       f[x][i]=f[f[x][i-1]][i-1]; 
    } 
    for(register int i=head[x];i;i=nxt[i]){
        if(to[i]==fa) continue;
        e=to[i];d[e]=d[x]+1;
        f[e][0]=x;
        dfs(e,x);
        sz[x]+=sz[e];
    }
    mx=max(mx,d[x]);
}

inline void df(int x)
{
    c[++dfn]=x;rem[x]=sz[x];int e;
    for(register int i=head[x];i;i=nxt[i]){
        e=to[i];
        df(e);
        if(!bel[e]) continue;
        int t1=dis(bel[e],x),t2=dis(bel[x],x);
        if(((t1==t2)&&bel[e]<bel[x])||(t1<t2)||(!bel[x])) bel[x]=bel[e];
    }
}

inline void dff(int x)
{
    int e;
    for(register int i=head[x];i;i=nxt[i]){
        e=to[i];
        int t1=dis(bel[e],e),t2=dis(bel[x],e);
        if(((t1==t2)&&bel[x]<bel[e])||(t2<t1)||!bel[e]) bel[e]=bel[x];
        dff(e); 
    }
}

inline void solve(int a,int b)
{
     int ct=b,x=b;
     for(register int i=flor;i>=0;i--){
        if(d[f[x][i]]>d[a]){
            x=f[x][i];
        }
     }  
     rem[a]-=sz[x];
     if(bel[a]==bel[b]){
        ans[bel[a]]+=sz[x]-sz[b];
     }else{
        int e;
        for(register int i=flor;i>=0;i--){
            e=f[ct][i];
            if(d[e]<=d[a]) continue;
            int t1=dis(bel[a],e),t2=dis(bel[b],e);
            if(((t1==t2)&&(bel[b]<bel[a]))||(t1>t2)) ct=e;
        }
        ans[bel[a]]+=(sz[x]-sz[ct]);
        ans[bel[b]]+=(sz[ct]-sz[b]);
     }
}

int main(){
    n=read();
    for(register int i=1;i<n;i++){
        int u=read(),v=read();
        lk(u,v);lk(v,u);
    }
    dfs(1,0);
    flor=(int)(log(mx)/log(2));
    if((1LL<<flor)<mx) flor++;
    memset(head,0,sizeof(head));
    Q=read();
    while(Q--){
        dfn=0;top=0;tot=0;
        m=read();
        for(register int i=1;i<=m;i++){
            a[i]=read();b[i]=a[i];
            bel[a[i]]=a[i];
        }
        sort(a+1,a+m+1,cmp);
        if(bel[1]!=1) s[++top]=1;
        for(register int i=1;i<=m;i++){
           int t=a[i],p=0;
           while(top>0){
              p=lca(s[top],t);
              if(top>1 && d[p]<d[s[top-1]]){
                lk(s[top-1],s[top]);top--;
              }else if(d[p]<d[s[top]]){
                lk(p,s[top]);top--;break;
              }else break;
           }
           if(s[top]!=p) s[++top]=p;
           s[++top]=t;
         }
         while(top>1){lk(s[top-1],s[top]);top--;}
           df(1);dff(1);
           for(register int i=1;i<=dfn;i++){
            for(register int j=head[c[i]];j;j=nxt[j]){
                solve(c[i],to[j]);
            }
           }
           for(register int i=1;i<=dfn;i++) 
             ans[bel[c[i]]]+=rem[c[i]];
           for(register int i=1;i<=m;i++) printf("%d ",ans[b[i]]);
           printf("\n");
           for(register int i=1;i<=dfn;i++){
              ans[c[i]]=bel[c[i]]=rem[c[i]]=head[c[i]]=0;
           }
        } 
        return 0;
}

ps:代码参照hzwer

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页