题解:P13680 [IAMOI R2] 未送出的花

题目Link

树形 DP,时间复杂度 \(O(n^2)\),主要解答一些细节和疑惑,篇幅看起来长了点但是只是详细了一点而已。

方案中节点 \(u\) 的盛开度为 \(val_u\)

性质 1:最优情况下,所有节点 \(u\) 的任意祖先盛开度都比 \(u\) 的盛开度要大。

证明:假设 \(a\)\(b\) 的若干级祖先,且 \(val_a < val_b\),并且考虑 \(a\to b\) 路径上任意一节点 \(v\),如果交换 \(val_a,val_b\) 则会使得对于点 \(v\) 能取到的中位数可能变大,答案一定是不劣的。

考虑通过性质 1 对问题进行转化,则对于深度为 \(dep_u\) 的节点 \(u\) 取到的中位数一定是在其祖先链上第 \(\lceil \frac{dep_u}{2}\rceil\) 个点(不妨令其为 \(p\))上的 \(val_p\)。对于每个 \(p\) 我们统计它被多少 \(u\) 选中,记为 \(cnt_p\)

性质 2:所有被选中的点 \(p\) 构成一个包含根节点 \(1\) 的联通块。

证明:考虑一个点 \(u\) 如果能选中一个点 \(p\),则说明该点的父亲也会选中一个在 \(1\to p\) 路径上的节点,\(1\to u\) 上所有节点选择的节点就能够覆盖 \(1\to p\) 上的所有节点。考虑树中所有叶子节点 \(u\),它到根的链上 \(1\to p\) 这一部分就是被选择的节点,所有 \(1\to p\) 的链重合使得 \(1\) 能够到达联通块内所有节点。

那么现在问题就变成了:

给定一个 \(k\),求选择 \(num\) 个节点构成包含 \(1\) 的联通块,其权值 \(\sum cnt\geq k\),且使 \(num\) 最小化(由于只需要填 \(num\) 个点,答案即为 \(n-num+1\)

有一个简单的思路,维护一个关于 \(cnt_u\) 的大根堆,先把 \(1\) 入堆,然后每次取出堆顶,将目前总共能覆盖的节点数量加上 \(cnt_u\) 贪心地将相连节点都入堆。这样显然是错误的。

性质 3:在 \(k=1\dots n\) 的情况中,\(k=a-1\) 对于 \(k=a\) 不具可差分性。

证明:容易发现,做 \(k=a-1\) 是存在后效性的,这也是为什么我们不能使用大根堆直接做的原因。考虑一个 hack,存在一个节点 \(u\)\(val\) 具有极大值,但是其父亲的 \(val_{fa}\) 具有极小值,那么在做大根堆的时候我们肯定会先入堆若干个与 \(fa\) 同级的且 \(val\)\(fa\) 大的节点,这样会使得需要覆盖的节点数大幅增加,但是如果直接先取 \(fa\) 再取 \(u\) 就可以只增加 \(2\) 个需要节点然后解决后续的问题。

考虑正解,虽然连通块形态是不可差分的,但是对于每个 \(k\) 我们选到的联通块,其大小一定是随着 \(k\) 的需求增加而不降的,相应的选到的联通块大小 \(j\) 也一定对应着一段覆盖的 \(k\) 的区间。不妨令 \(dp_{i,j}\) 表示以 \(i\) 为根的子树中选了包含 \(i\) 的联通块大小为 \(j\) 所能取到最大的 \(\sum cnt\),初始化 \(dp_{u,1}=cnt_u\),转移是 \(O(n^3)\) 的,利用 \(siz_u\) 限制可以做到 \(O(n^2)\),令 \(v\)\(u\) 的儿子,则 \(dp_{u,sum}=\max \sum dp_{v,b_v}(\sum b_v=sum)\),转移是树形 DP 的基础内容,这里不做展开。

最后 \(dp_{1,j}\) 可以覆盖 \([dp_{1,j-1}+1,dp_{1,j}]\) 上所有 \(k\) 的值,相应计算即可。实际上大部分情况做树形 DP 时只需要遍历约一半的节点,因为有用的(\(cnt_u>0\))的节点也差不多是这个数,当然要是构造出一个菊花那就是另一回事了,时间还是比较优秀的。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
vector<int>G[N];
int n,cnt[N],rcnt;
int stk[N],tp;
int dp[N][N],siz[N];
int ans[N];
void dfs(int u,int fa){
	stk[++tp]=u;
	cnt[stk[(tp+1)/2]]++;
	for(int v:G[u]){
		if(v==fa)continue;
		dfs(v,u);
	}
	stk[tp--]=0;
	if(u)rcnt++;
}
void dfs1(int u,int fa){
	if(!cnt[u])return ;
	siz[u]=1;dp[u][0]=0;
	dp[u][1]=cnt[u];
	for(int i=2;i<=rcnt;i++)
		dp[u][i]=0;
	for(int v:G[u]){
		if(v==fa)continue;
		if(!cnt[v])continue;
		dfs1(v,u);
		for(int i=siz[u];i>=1;i--)
			for(int j=siz[v];j>=0;j--)
				dp[u][i+j]=max(dp[u][i+j],dp[u][i]+dp[v][j]);
		siz[u]+=siz[v];
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--){
		cin>>n;rcnt=0;
		for(int i=1;i<=n;i++)
			G[i].clear(),
			cnt[i]=0;
		for(int i=1;i<n;i++){
			int u,v;cin>>u>>v;
			G[u].push_back(v);
			G[v].push_back(u);
		}
		dfs(1,0);
		dfs1(1,0);
		int now=0;
		for(int i=1;i<=n;i++)
			while(now<dp[1][i])ans[++now]=n-i+1;
		for(int i=1;i<=n;i++)
			cout<<ans[i]<<' ';
		cout<<'\n';
	}
	return 0;
}
posted @ 2025-08-11 10:46  TBSF_0207  阅读(25)  评论(0)    收藏  举报