ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SP2420 题解

2022-08-13 21:01:18  阅读:116  来源: 互联网

标签:重心 fiz 题解 sum SP2420 leq siz 节点


SP2420 solution

给定一颗 \(n\) 个节点的树,在树上找一条长为 \(l\) 的链,使得树上每个节点到链的距离之和最短,求这个最短距离。

题解

首先我们思考多个点到一个点距离和怎么计算。可以考虑使用树形 DP,将这个点作为跟,记录 \(siz_u\) 为 \(u\) 点子树的大小,\(sum_u\) 为 \(u\) 点子树中的点到它的距离和。则如果存在 \(v\) 是 \(u\) 的儿子,可以得到转移

\[sum_u\gets siz_v+sum_v \]

不妨设这条链过根节点,那么如果点 \(v\) 在链上,则答案减小 \(siz_v\),因为 \(v\) 子树内的点(包括它自己),不用走 \(v\to u\) 这条边了,而这条边会被计算 \(siz_v\) 次。对于 \(v\) 的子节点这一结论同样成立。

假如这题让我们求的不是一条链,而是一个点,那么这个点就是这颗树的重心。

证明:

对于假设重心为 \(u\) 且是树的根节点,在重心旁的点为 \(v\),这是一个换根的过程,定义 \(f_i\) 表示 \(i\) 为根节点时各点到它距离和。

那么

\[f_v=f_u+n-2\times siz_v \]

因为 \(u\) 是重心,所以 \(2\times siz_v\leq n\),所以 \(f_v\geq f_u\)。

将这个结论推广到所有节点,假设 \(w\) 是 \(v\) 节点的儿子,必然 \(siz_w<siz_v\),自然 \(f_w> f_v\)。

我们将这个过程搬到链上,如图。

假设绿色链过重心,红色链不过重心。注意此时重心是根。

那么显然红色链和绿色链公共的部分的子节点的答案不变,而因为重心的性质,\(siz_v\leq siz_w\leq \frac{n}{2}\leq siz_u-siz_w\),那么答案变大 \(siz_u-siz_w-siz_v\geq 0\)。

所以我们不严谨地说明了这条链必须经过重心。

实现

树形 DP 先求出树的重心,然后以重心为根节点求出每个点到根(不包含根)的路径上的 \(siz\) 和,同时求出每个点的深度(假设根节点深度为 \(0\))。

然后答案就是两个在不同子树内的点,他们的深度之和小于等于 \(l\),他们 \(siz\) 和的和。

注意如果 \(l\) 很大,那么如果用桶的话可能会出现 \(l\) 减去一个点的深度后桶内没有值的情况,于是我们用树状数组统计前缀最大值即可。

时间复杂度 \(O(n\log l)\)。

int n,L,root,fa[N];
ll sum[N],siz[N],fiz[N];
vector<int>edge[N];
void getroot(int u,int f){
	siz[u]=1,fiz[u]=0;
	for(auto v:edge[u]){
		if(v==f)continue;
		getroot(v,u);
		siz[u]+=siz[v];
		fiz[u]=max(fiz[u],siz[v]);
	}fiz[u]=max(fiz[u],n-siz[u]);
	if(fiz[u]<fiz[root])root=u;
}
int lis[N],tot,dep[N];
void dfs(int u,int f){
	siz[u]=1,sum[u]=0;
	for(auto v:edge[u]){
		if(v==f)continue;
		dfs(v,u);
		siz[u]+=siz[v];
		sum[u]+=sum[v]+siz[v];
	}
}
void getlis(int u,int f,int d,ll pre){
	siz[u]+=pre;dep[u]=d;lis[++tot]=u;
	for(auto v:edge[u]){
		if(v==f)continue;
		getlis(v,u,d+1,siz[u]);
	}
}
struct BIT{
	ll c[102];
	inline void init(){memset(c,0,sizeof(c));}
	inline int lowbit(int x){return x&-x;}
	inline void update(int i,ll v){++i;for(;i<=L+1;i+=lowbit(i))c[i]=max(c[i],v);}
	inline ll getmaxv(int i){ll ans=0;++i;for(;i;i-=lowbit(i))ans=max(ans,c[i]);return ans;}
}c;
inline ll solve(){
	int u=root;ll ans=0;
	dfs(u,0);c.init();
	for(auto v:edge[u]){
		tot=0;getlis(v,u,1,0);
		for(int i=1;i<=tot;++i)
			if(dep[lis[i]]<=L)ans=max(ans,siz[lis[i]]+c.getmaxv(L-dep[lis[i]]));
		for(int i=1;i<=tot;++i)
			if(dep[lis[i]]<=L)c.update(dep[lis[i]],siz[lis[i]]);
	}
	return sum[u]-ans;
}
int main(){
	int T=read();
	while(T--){
		n=read(),L=read();
		for(int i=0;i<=n;++i)
			edge[i].clear();
		for(int i=1;i<n;++i){
			int u=read()+1,v=read()+1;
			edge[u].push_back(v);
			edge[v].push_back(u);
		}
		fiz[root=0]=INF;
		getroot(1,0);
		printf("%lld\n",solve());
	}
	return 0;
}

后记

感觉生了点病,但是不能及时去医院,写道 hospital 题。感谢 Kzos_017ctldragon 对做此题的帮助。

这里贴一下队爷 Kzos_017 的做法。(不懂)

标签:重心,fiz,题解,sum,SP2420,leq,siz,节点
来源: https://www.cnblogs.com/BigSmall-En/p/16584000.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有