ATR175D LIS on Tree II 学习笔记

ATR175D LIS on Tree II 学习笔记

Luogu Link

题意简述

给定一棵 \(n\) 个结点,以 \(1\) 为根的树。需要给每个点 \(i\) 赋一个 \(w_i\in [1,n]\),且满足 \(\forall i\neq j,w_i\neq w_j\)

定义一个结点的得分 \(p_i=\text{LIS}(1\to i)\)。试给出一种赋值方案使 \(\sum_{i=1}^n p_i=m\),或报告无解。

\(n\le 2\times 10^5,m\le 10^{11}\)

做法解析

判无解是很容易的。问题有解当且仅当 \(m\in [n,\sum \text{dep}(u)]\)(其中 \(\text{dep}(1)=1\))。

有至少一半的构造题都是可以构造出方案,取到上下界间的任意值的。这题我们也来思考如何稳定构造出解。

我们尝试分析填数时每个数会带来的贡献。LIS这种从一个前缀一步步扩展而来的问题,应当适合从根往下地分析。

令点 \(u\) 的父亲为 \(fa\)。若 \(w_u>\max_{i\in [1\to fa]} w_i\),则 \(w_u\) 子树内所有结点的答案都将加一,否则子树内所有结点的答案则不增不减。这类似于一个背包问题,每个结点相当于一个价值为 \(siz_u\) 的物品,我们最后要选出总价值为 \(m\) 的东西。

容易发现能够选出的总价值 \(\sum v\in [0,\sum siz]\)

这里证明一下:

我们知道,往一个能用子集和恰好表示出 \([0,r]\) 里面加一个元素 \(x\),那么新的集合能用子集和表示出的区间就是 \([0,r]\cup[x,n+x]\),新区间仍然连续当且仅当 \(x\le r+1\)。此结论在LGP4587中亦有记载。

我们先考虑儿子后考虑父亲地将 \(siz_u\) 一个一个加到集合 \(S\) 里面。第一个加的一定是一个叶子结点,子集和可表示值域区间从 \([0,0]\) 变为 \([0,1]\)。之后我们发现,加叶子结点一定不破坏可表示值域区间的连续性,而加一个非叶子结点 \(u\) 时,一定已经有 \(siz_u-1\) 个结点被添加,也就是说此时必然有 \(r\ge siz_u-1\),也即 \(siz_u\le r+1\)。所以我们归纳地证明了我们一定可以构造出来。

另外,由LIS的意义可知根结点要视为是必选的。所以到这里我们也从另一种视角证明了上述的上下界。

然后我们具体怎么凑出 \(m\) 呢?正着做不太好做,但反着做容易很多,我们先默认所有结点都选上,然后再从根往下一层一层地考虑,每层里面价值从大到小地,如果删掉当前结点的贡献后总和仍然大于等于 \(m\) 就删除之。

这么做是对的,原因是我们从粗粒度物往细粒度物考虑一定更优,而且这题的粗粒度物总是可表示为若干细粒度物之和。

好了我们已经确定哪些点要造成贡献了,怎么构造答案呢?这一步实则不难。

为了保证没取到的点不造成贡献,我们令所有没取到的点的权值都小于所有取到的点的权值。为了保证没取到的点不会自己形成一段LIS,我们让所有没取到的点的权值都从根往下降序排列就行。同理,既然取到的点要形成LIS,那么这些点的权值就应该从根往下升序排列。

时间复杂度 \(O(n\log n)\),瓶颈在排序上。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=2e5+5;
int N,X,Y;lolo M,sum;
vector<int> Tr[MaxN];
void addudge(int u,int v){
    Tr[u].push_back(v);
    Tr[v].push_back(u);
}
int siz[MaxN];pii P[MaxN];
void dfs1(int u,int f){
    siz[u]=1;
    for(int v : Tr[u]){
        if(v==f)continue;
        dfs1(v,u),siz[u]+=siz[v];
    }
    P[u]={-siz[u],u},sum+=siz[u];
}
int vis[MaxN],A[MaxN],tot;
void dfs2(int u,int f){
    for(int v : Tr[u])if(v!=f)dfs2(v,u);
    if(!vis[u])A[u]=++tot;
}
void dfs3(int u,int f){
    if(vis[u])A[u]=++tot;
    for(int v : Tr[u])if(v!=f)dfs3(v,u);
}
int main(){
    readis(N,M);
    for(int i=1;i<N;i++)readis(X,Y),addudge(X,Y);
    dfs1(1,0);if(M<N||M>sum){puts("No");return 0;}
    puts("Yes"),sort(P+1,P+N+1);
    for(int i=1;i<=N;i++){
        auto &[fi,se]=P[i];
        if(M+fi>=0)vis[se]=1,M+=fi;
    }
    dfs2(1,0),dfs3(1,0);
    for(int i=1;i<=N;i++)writip(A[i]);
    return 0;
}
posted @ 2025-05-03 20:41  矞龙OrinLoong  阅读(11)  评论(0)    收藏  举报