题意:给定一颗树,每个叶子节点 \(u\) 都有权值 \(val[u]\),求每个非叶子节点子树的最小叶子距离,若该子树只有一个叶子节点,输出 INF

貌似本来是一道树分治 (并不会) 的题目,然而可以利用平衡树进行离线合并,边统计边更新

一开始没有想到这种方法,看了别人家的代码后觉得真是清晰明了

set 交换后无需额外在 dfs 维护和叶节点更新后置为 INF 满足题意的单一叶子情况确实是不错的 trick,值得学习

#include<bits/stdc++.h>
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define rrep(i,j,k) for(register int i=j;i>=k;i--)
#define erep(i,u) for(register int i=head[u];~i;i=nxt[i])
#define print(a) printf("%lld",(ll)(a))
#define println(a) printf("%lld\n",(ll)(a))
#define printbk(a) printf("%lld ",(ll)(a))
using namespace std;
const int MAXN = 5e4+11;
const int INF = 0x7fffffff;
typedef long long ll;
ll read(){
    ll x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN],tot;
set<ll> s[MAXN];
ll n,m,val[MAXN];
void init(){
    memset(head,-1,sizeof head);
    tot=0;
}
void add(int u,int v){
    to[tot]=v;
    nxt[tot]=head[u];
    head[u]=tot++;
    swap(u,v);
    to[tot]=v;
    nxt[tot]=head[u];
    head[u]=tot++;
}
ll merge(set<ll> &a,set<ll> &b){
    if(a.size()<b.size()) swap(a,b);
    set<ll>::iterator it,lb,ub;
    ll mn=INF;
    for(it=b.begin();it!=b.end();it++){
        lb=ub=a.lower_bound(*it);
        if(lb!=a.begin()/*&&lb!=a.end()*/)lb--; // 多了会 WA
        if(lb!=a.end()) mn=min(mn,abs((*it)-(*lb)));
        if(ub!=a.end()) mn=min(mn,abs((*it)-(*ub)));
        a.insert(*it);//
    }
    b.clear();
    return mn;
}
#define isleaf(x) (bool((x)>=n-m+1&&(x)<=n))
void dfs(int u,int p){
    if(isleaf(u)){
        s[u].insert(val[u]);
        val[u]=INF;//trick
        return;
    }
    erep(i,u){
        int v=to[i];
        if(v==p)continue;
        dfs(v,u);
        val[u]=min(val[u],val[v]);
        val[u]=min(val[u],merge(s[u],s[v]));
    }
}
int main(){
    while(cin>>n>>m){
        init();
        rep(i,1,n) val[i]=INF;
        rep(i,1,n) s[i].clear();
        rep(i,2,n) add(read(),i);
        rep(i,n-m+1,n) val[i]=read();
        dfs(1,-1);
        rep(i,1,n-m){
            if(i==n-m) println(val[i]);
            else printbk(val[i]);
        }
    }
    return 0;
}