1. 程式人生 > 其它 >AIM Tech Round 3 (Div. 1)]C.Centroids

AIM Tech Round 3 (Div. 1)]C.Centroids

AIM Tech Round 3 (Div. 1)C.Centroids

題意:題意:給一棵樹,問樹的每個點能不能通過僅一次刪邊加邊變成質心,所謂質心即刪掉該點和相鄰的邊剩下的每個聯通塊大小都小於等於n/2。n ≤ 400000
思路:樹形dp+換根dp
$umax[i]表示除了以點i為根的樹,整顆大樹的剩餘部分中,結點數最多的子樹的值$
$son[i]表示以點i為根的樹的總結點數$
$uson[i]表示除了以點i為根的樹,其餘的總結點數$
$fi[i],se[i]表示以點i為根的樹中,權值最大和次大的兩顆子樹對應的值$
$flag[i]標記以點i為根的樹對應3種情況:為0表示所連子樹中沒有一個是超過n/2,可以得出該點一定可以為重心。$
$為-1 表示上方子樹超過了n/2。為確切數字表示所連子樹中超過了n/2$

對於每一個點,將其看為一棵樹的根,如果這棵樹的所有子樹的結點數都小於等於$n/2$時,這個點總是可以成為重心的
如果這棵樹的子樹中有一棵子樹 $ t $ 的結點數大於$n/2$,那麼我們需要去判斷如果把子樹 $t$ 的$fi[t]$刪去,連線到點 $i$ 是否可行
如果除了以點 $i$ 為根的樹,整顆大樹的剩餘部分中,結點數最多的子樹$t$的值大於 $n/2$ ,
同樣判斷如果把子樹 $t$ 的$fi[t]$刪去,連線到點 $i$ 是否可行。

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef  long long ll;
const int inf=0x3f3f3f3f;
const int N =4e5+7,M=8e5+7;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
    e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
int n,umax[N],son[N],uson[N],fi[N],se[N],flag[N],ans[N];
void dfs1(int u,int fa){
    son[u]=1;uson[u]=1;
    umax[u]=1;
    for(int i=h[u];i!=-1;i=ne[i]){
        int v=e[i];
        if(v==fa)continue;
        dfs1(v,u);
        son[u]+=son[v];
        if(son[v]>n/2)flag[u]=v;
        if(son[v]<=n/2){
            if(son[v]>fi[u])se[u]=fi[u],fi[u]=son[v];
            else if(son[v]>se[u])se[u]=son[v];
        }
        else {
            if(fi[v]>fi[u])se[u]=fi[u],fi[u]=fi[v];
            else if(fi[v]>se[u])se[u]=fi[v];
        }
    }
    uson[u]=n-son[u];
    if(uson[u]>n/2)flag[u]=-1;
}
void dfs2(int u,int fa){
    if(uson[u]<=n/2){
        umax[u]=uson[u];
    }
    else if(fi[fa]==son[u]){
        umax[u]=max(umax[fa],se[fa]);
    }
    else {
        umax[u]=max(umax[fa],fi[fa]);
    }
    for(int i=h[u];~i;i=ne[i]){
        int v=e[i];
        if(v==fa)continue;
        dfs2(v,u);
    }
}
int main(){
    cin>>n;
    memset(h,-1,sizeof h);
    memset(umax,1,sizeof umax);
    for(int i=1;i<n;i++){
        umax[i]=1;
        int a,b;cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    dfs1(1,-1);
    dfs2(1,-1);
    for(int i=1;i<=n;i++){
        if(flag[i]==0)cout<<"1 ";
        else if(flag[i]==-1&&uson[i]-umax[i]<=n/2)cout<<"1 ";
        else if(flag[i]!=-1&&son[flag[i]]-fi[flag[i]]<=n/2)cout<<"1 ";
        else cout<<"0 ";
    }
    cout<<endl;
}