長鏈剖分模板

題目鏈接:https://codeforces.com/contest/1009/problem/F

題目大意:給你一棵樹,定義d(x,i)表示x子樹內和x距離爲i的節點數,對每個x求使d(x,i)最大的i,如有多個輸出最小的。

長鏈剖分是一種類似dsu on tree的小trick,可以把維護子樹中只與深度有關的信息做到線性的時間複雜度。

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define mp make_pair
#define pb push_back
#define ll long long
using namespace std;
const int N = 1e6+1;
vector<int>nxt[N];
int son[N],len[N];
void split(int u,int f) {
    son[u] = 0;
    len[u] = 1;
    for(auto v:nxt[u]) {
        if(v==f)continue;
        split(v,u);
        if(len[son[u]]<len[v]) son[u] = v,len[u] = len[v]+1;
    }
}
//---------------------
ll pos[N],dp[N],ans[N],cnt;       //空間公用,將dp數組從二維壓成一維
void dfs(int u,int f) {           //dp[u][i] 表示u這顆子樹內距離u爲i的點的個數
    pos[u] = ++cnt;
    dp[pos[u]] = 1;                                     //dp[u][0] = 1; 
    if(son[u]) dfs(son[u],u),ans[u] = ans[son[u]]+1;    //繼承重兒子的答案
    for(auto v:nxt[u]) {
        if(v==f||v==son[u]) continue;
        dfs(v,u);
        rep(j, 0, len[v]-1) {
            dp[pos[u]+j+1] += dp[pos[v]+j];             //dp[u][j+1] += dp[v][j];
            if(dp[pos[u]+j+1]>dp[pos[u]+ans[u]]) ans[u] = j+1; 
            else if(dp[pos[u]+j+1]==dp[pos[u]+ans[u]]) ans[u] = min(ans[u],1ll*j+1);
        }
    }
    if(dp[pos[u]+ans[u]]==1) ans[u] = 0;                //特判
}
int main() {
    //freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    int n;
    cin>>n;
    rep(i, 1, n-1) {
        int u,v;
        cin>>u>>v;
        nxt[u].pb(v);
        nxt[v].pb(u);
    }
    split(1,0);
    dfs(1,0);
    rep(i, 1, n) cout<<ans[i]<<endl;
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章