【数据结构】点分治

一.介绍

 点分治(Centroid Decomposition)是一种树分治的技术,主要用于解决树上路径问题。在树结构中,点分治的目标是将原树分解为若干棵子树,使得每个子树的大小都不超过原树大小的一半。这样的分解可以有效地减小问题的规模,从而提高算法的效率。

点分治的基本思想是选择一个合适的树节点作为"重心"(Centroid),然后以该节点为根进行递归处理。选取重心的方法是找到使得删除该节点后最大子树的大小最小的节点,这样可以保证分解后的子树规模较为平衡。

点分治的步骤如下:

  1. 选择一个节点作为当前子树的重心。
  2. 对于以重心为根的子树,进行路径问题的处理。
  3. 递归处理重心的每个子树。

点分治的应用范围很广,常见的问题包括最近公共祖先(LCA)、树上路径权值和等。


二.例题 

P4178 Tree - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)


三.讲解

【【AgOHの算法胡扯】点分治】https://www.bilibili.com/video/BV1PE41197md?vd_source=20176e3c6ed0103d83cbfbb40abe39fe


四.AC

#include<bits/stdc++.h>
#define maxn 40005
using namespace std;
int n,k;
long long ans;
struct Edge{
	int u,v,w,next;
}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v,int w){
	edge[++cnt]=(Edge){u,v,w,head[u]}; head[u]=cnt;
}
int dp[maxn],size[maxn],root,sum; //找重心时用 
bool vis[maxn];
void getroot(int u,int fa){
	size[u]=1; dp[u]=0;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].v;
		if(v==fa || vis[v]) continue;
		getroot(v,u);
		size[u]+=size[v];
		dp[u]=max(dp[u],size[v]);
	}
	dp[u]=max(dp[u],n-size[u]);
	if(dp[u]<dp[root]) root=u;
}
int dis[maxn],res[maxn],tot;
void getdis(int u,int fa){
	res[++tot]=dis[u];  //之后排序用
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].v;
		if(v==fa || vis[v]) continue;
		dis[v]=dis[u]+edge[i].w;
		getdis(v,u);
	} 
}
int doit(int u,int w){
	tot=0;dis[u]=w;getdis(u,0);
	sort(res+1,res+tot+1);
	int l=1,r=tot,ans=0;
	while(l<=r) (res[l]+res[r]<=k) ? (ans+=r-l,l++) : (--r);
	return ans;
}
void solve(int u){
	vis[u]=1; ans+=doit(u,0);
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].v;
		if(vis[v]) continue;
		ans-=doit(v,edge[i].w); //重复的减掉 
		sum=size[v];  //注意,在子树情况下,树节点的总数是变化的
		dp[0]=n; root=0;
		getroot(v,u); solve(root);
	}
}
int main(){
	scanf("%d",&n);
	int x,y,z;
	for(int i=1;i<n;i++){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}
	scanf("%d",&k);
	dp[0]=sum=n; getroot(1,0);
	solve(root);
	printf("%lld",ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值