题目描述
给一棵 \(n\) 个节点的树,将 \(1 \sim n\) 的排列填入节点内,使得根节点到每个节点的简单路径的权值 LIS 长度和为 \(K\),给出构造。
思路
根据 LIS 的性质有:
- \(L_1 = 1\)
- \(L_{fa_u} \le L_u \le L_{fa_u}+1\)
只要 \(L\) 满足上述条件,就一定存在一种构造使其满足对应 \(L\) 的值。
我们首先令 \(L\) 取其下界,即 \(\forall i,L_i = L_{fa_i}\)。
然后考虑令 \(L_u = L_{fa_u}+1\) 会产生什么影响。不难发现,此时 \(u\) 子树内所有节点的下界都必须加一,即 \(\sum L_i\) 必须加 \(u\) 的子树大小。
现在问题变为,我们可以选择一些子树,使其内 \(L\) 都加一,使得 \(\sum L_i = K\)。
直接按子树大小排序,然后贪心能选就选。
我们现在求出了 \(L\) ,考虑怎么按其进行构造。
对于节点 \(u\),为了使其前面有足够多比其权值小的数,以此凑够 \(L_u\),所以 \(L_u\) 越大 \(v_u\) 也应该越大。
按 \(L\) 升序排序,依次填入 \(n \sim 1\) 即可。
有一个细节,在 \(L\) 相等的一个连通块内,为了不让较深节点的 \(L_u\) 变大,则应使得 \(v_{fa_u} > v_u\)。
代码
const int N = 2e5+10;
int n,dtop; ll k;
struct{int to,nex;
}edge[N<<1];
int head[N],edge_num;
inline void add(int x,int y){edge[++edge_num].to = y;edge[edge_num].nex = head[x];head[x] = edge_num;
}
struct node{int siz,pos;inline int operator < (const node&G) const{return siz < G.siz;}
}g[N];
inline void dfs1(int now,int fu){g[now] = {1,now};for(int i=head[now];i;i=edge[i].nex){int tto = edge[i].to;if(tto==fu) continue;dfs1(tto,now);g[now].siz += g[tto].siz;}
}
struct WP{int L,pos,dfn;inline int operator < (const WP&G){if(L^G.L) return L > G.L;return dfn < G.dfn;}
}wp[N];
inline void dfs2(int now,int fu){wp[now].L += wp[fu].L;wp[now].pos = now;wp[now].dfn = ++dtop;for(int i=head[now];i;i=edge[i].nex){int tto = edge[i].to;if(tto==fu) continue;dfs2(tto,now);}
}
int ans[N];
int main(){read(n,k);if(k<n){puts("No");return 0;}for(int i=1,u,v;i<n;++i){read(u,v);add(u,v);add(v,u);}dfs1(1,0);sort(g+1,g+1+n); // 按子树大小排序之后贪心for(int i=n;i && k;--i){if(k>=g[i].siz){k -= g[i].siz;++wp[g[i].pos].L; // 对 L 差分}}if(k){ // 所有子树都选了还是到不了 Kputs("No");return 0;}dfs2(1,0);sort(wp+1,wp+1+n);// 按 L 排序和 dfn 排序for(int i=1;i<=n;++i) ans[wp[i].pos] = n-i+1;puts("Yes");for(int i=1;i<=n;++i) printf("%d ",ans[i]);return 0;
}