CSP2019树的重心

题解:

CSP2019D2T3

首先我们要明确一个性质,那就是对于一棵树的任何一个节点来说,如果这个点不是重心,那么这棵树的重心就一定在这个节点的以重儿子为根节点的子树里

证明显而易见,因为该点不是重心所以siz[重儿子]一定大于$\lfloor \frac{siz[x]}{2} \rfloor$

另外还有一个重心的定义:重心所有子树的大小一定小于等于$\lfloor \frac{siz[重心]}{2} \rfloor$

我们首先遍历整棵树,切断一条边(u,v)后,我们得到以u为根、以v为根的两颗树

然后不断向重儿子跳,直到找到重心

这里我们可以用倍增实现,时间复杂度$O(N log_{2} N)$

我们预处理倍增数组fv[x][i],然后对于以v为根的子树,直接倍增找重儿子即可

对于以u为根的子树,也可以用类似的方法解决,我们只需要让u的父亲变成u的儿子,即f[fa]=u,hv[u][0]=siz[hv[u][0]]<siz[fa]-siz[u]?fa:hv[u][0],然后就好了

其实这个题还蛮难的 

CODE:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dwn(i,a,b) for(int i=a;i>=b;i--)
#define MAXN 300004
using namespace std;
typedef long long ll;
ll ans;
int T,n,tot,nxt[MAXN<<1],to[MAXN<<1],fir[MAXN],hv[MAXN][20],f[MAXN],siz[MAXN],shv[MAXN],thv[MAXN],tsiz[MAXN],S;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while('0'>ch || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while('0'<=ch && ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x*f;
}
void ade(int x,int y){
    to[++tot]=y;
    nxt[tot]=fir[x];
    fir[x]=tot;
}
void dfs1(int x,int fa){
    f[x]=fa; hv[x][0]=shv[x]=thv[x]=0; tsiz[x]=1;
    for(int k=fir[x];k;k=nxt[k]){
        if(to[k]==fa) continue;
        dfs1(to[k],x);
        tsiz[x]+=tsiz[to[k]];
        if(tsiz[to[k]]>tsiz[thv[x]]) shv[x]=thv[x],thv[x]=to[k];
        else if(tsiz[to[k]]>tsiz[shv[x]]) shv[x]=to[k];
    }
    hv[x][0]=thv[x]; siz[x]=tsiz[x];
}
void init(){
    rep(i,1,19) rep(j,1,n) hv[j][i]=hv[hv[j][i-1]][i-1];
}
bool chk(int x,int y){
    if(!x) return 0;
    return ((max(siz[hv[x][0]],y-siz[x])*2)<=y);
}
void recalc(int x){
    rep(i,1,19) hv[x][i]=hv[hv[x][i-1]][i-1];
} 
void clear(){
    memset(fir,0,sizeof(fir));
    tot=ans=0;
}
void dfs2(int x,int y){
    int now;
    for(int k=fir[x];k;k=nxt[k]){
        if(to[k]==y) continue;
        if(to[k]==thv[x]) hv[x][0]=shv[x];
        else hv[x][0]=thv[x];
        if(siz[y]>siz[hv[x][0]]) hv[x][0]=y;
        recalc(x);
        siz[to[k]]=tsiz[to[k]]; siz[x]=S-tsiz[to[k]];
        now=x;
        dwn(i,19,0) if(hv[now][i] && siz[x]-siz[hv[now][i]]<=(siz[x]/2)) now=hv[now][i];
    //    cout<<x<<" "<<now<<"FDF"<<" "<<f[now]<<" "<<f[now]*chk(f[now],siz[x])<<endl;
        ans=ans+now*chk(now,siz[x])+f[now]*chk(f[now],siz[x]);
        now=to[k];
        dwn(i,19,0) if(hv[now][i] && siz[to[k]]-siz[hv[now][i]]<=(siz[to[k]]/2)) now=hv[now][i];
        ans=ans+now*chk(now,siz[to[k]])+f[now]*chk(f[now],siz[to[k]]);
        f[x]=to[k];
        dfs2(to[k],x);
    }
    siz[x]=tsiz[x]; hv[x][0]=thv[x]; f[x]=y;
    recalc(x);
    
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        clear();
        rep(i,1,n-1){
            int x=read(),y=read();
            ade(x,y);
            ade(y,x);
        }
        dfs1(1,0);
        init();
        S=tsiz[1];
        dfs2(1,0); 
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2020-10-08 11:08  niolle  阅读(225)  评论(0编辑  收藏  举报