P5666 [CSP-S2019] 树的重心

 

/*
:::每次删一条边 分裂的两棵子树 重心 编号总和 

pre:
1.重心:去掉点及所连边后 |最大子树|<=[n/2](floor) 且只有1/2个重心 且一定是子父节点 
2.重心一定是最大子树最小的点 (只去掉了一个点) 
3.重心一定在 本结点或 重儿子的子树上
4.n-sizx<=[n/2] 重心一定满足(必要条件)具有单调性 dep < 越成立  -> 最深的节点 
5.max(n-sizx,siz[hs[x][0]])<=n/2(定义式,唯一的决定式)
 
6.重心到所有点距离和最短
7.删除一节点 重心最多移动 一个 节点

:::每次枚举点 与其 子节点 分离 边 形成两棵子树 寻找最深的满足n-sizx<=n/2的 节点 与 其 父节点 
并将 编号加入
1.check(n-sizx<=n/2) + upd(hs[x][i]=hs[x][hs[x][i-1]][i-1])
2.dfs2每次删边 时  删 hs 更新成 phs 否则 仍然是 hs 用upd更新 每次搜索完所有 son 后 再恢复 原先状态     
3.dfs1->hs,phs,siz,fa
*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
//#include<queue>
//#include<vector>
#include<bits/stdc++.h>
#define ll long long
#define ddd printf("-----------------------\n");
using namespace std;
const int maxn=3e5+10 ;
const int mod=998244353;
const int inf=0x3f3f3f3f;

ll read(){
    ll x=0,F=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*F;
}

int n,head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
int hs[maxn][25],phs[maxn],fa[maxn],siz[maxn];
ll ans;

void add(int a,int b){
    to[++tot]=b,nxt[tot]=head[a],head[a]=tot;
}

void upd(int x){
    for(int i=1;i<=20;i++)
        hs[x][i]=hs[hs[x][i-1]][i-1];
}
int check(int x,int sizz){
    return max(sizz-siz[x],siz[hs[x][0]])<=sizz/2;
}

void dfs1(int x,int faa)
{
    siz[x]=1;fa[x]=faa; phs[x]=hs[x][0]=0;
    for(int i=head[x];i;i=nxt[i])
    {
        int v=to[i];if(v==faa) continue;
        dfs1(v,x);
        siz[x]+=siz[v];
        if(siz[hs[x][0]]<siz[v]) phs[x]=hs[x][0],hs[x][0]=v;//
        else if(siz[v]>siz[phs[x]]) phs[x]=v;
    }
}
void dfs2(int x,int faa)
{
    int prehs=hs[x][0],presiz=siz[x];
    for(int i=head[x];i;i=nxt[i])
    {
        int v=to[i];if(v==faa) continue;
        if(v==prehs) hs[x][0]=phs[x];
        else hs[x][0]=prehs;
        if(siz[faa]>siz[hs[x][0]]) hs[x][0]=faa;
        upd(x);
        
        int p=x;fa[x]=0,fa[v]=0,siz[x]=n-siz[v];
        for(int j=20;j>=0;j--)
            if(siz[x]-siz[hs[p][j]]<=siz[x]/2) p=hs[p][j];
        ans+=p+fa[p]*(check(fa[p],siz[x]));
        
        p=v;
        for(int j=20;j>=0;j--)
            if(siz[v]-siz[hs[p][j]]<=siz[v]/2) p=hs[p][j];
        ans+=p+fa[p]*(check(fa[p],siz[v]));
        
        fa[x]=v;//dfs v x的fa -> v
        dfs2(v,x);
    }
    hs[x][0]=prehs,siz[x]=presiz,fa[x]=faa; upd(x);
}

int main()
{
    ios::sync_with_stdio(false);
    int T;cin>>T;
    while(T--)
    {
        ans=0,tot=0;memset(head,0,sizeof(head));
        cin>>n;//n已经定义 会出错
        for(int i=1;i<n;i++){
            int u,v;cin>>u>>v;
            add(u,v),add(v,u);
        }
        dfs1(1,0);
        for(int k=1;k<=20;k++)
            for(int i=1;i<=n;i++)
                hs[i][k]=hs[hs[i][k-1]][k-1];
        dfs2(1,0);
        cout<<ans<<'\n';
    }

    return 0;
}

 

posted @ 2023-10-04 01:30  JMXZ  阅读(64)  评论(0)    收藏  举报