点分治(高级的坑就要专心填)

点分治是一种树分治算法
在解决树上路径满足某种属性的数量统计方面有着很大的作用

基本思想:

考虑到树上的路径对于一个点来说只有两种情况:
一是经过这个点,二是不经过这个点

对于不经过这个点的情况我们可以直接往下递归处理,主要问题就是解决经过这一个点的路径

如果一条路径要经过这个点,
那么ta必然是由两条在这个点两边不同子树中到这个点的路径组合而成

具体流程

First . 选取一个点,将无根树变成有根树

为了使每次的处理最优,我们通常要选取树的重心
树的重心,在之前的文章中有过介绍:
何为“重心”,就是要保证与此点连接的子树的结点数最大值最小

简单说一下重心求法:

  • 一次dfs,算出以每个点为根的子树大小
  • 记录以每个节点为根的最大子树大小
  • 判断:如果以当前结点为根更优,就更新当前根
void getroot(int now,int fa)
{
	size[now]=1;
	pre[now]=fa;
	f[now]=0;
	int maxx=0;
	for (int i=st[now];i;i=way[i].nxt)
	    if (way[i].y!=fa&&!vis[way[i].y])         //避免陷入死循环
	    { 
	    	getroot(way[i].y,now);
	    	size[now]+=size[way[i].y];
	    	f[now]=max(f[now],size[way[i].y]);    //最大子树 
		}
    f[now]=max(f[now],sum-size[now]);             //这个点连接的最大连通块 
	if (f[now]<f[root]) root=now;
}

Second . 处理连通块中通过根节点的路径

注意,是通过根节点的路径,所以后面要去掉同一子树内部的路径,即去重

Third . 标记根节点

相当于处理后,将根节点从子树中删除

Fourth . 递归处理当前点为根的每棵子树

int solve(int x)
{
	vis[x]=1;                //将当前点标记
	for (int i=st[x];i;i=way[i].nxt)
	    if (!vis[way[i].y])
		{
			root=0;          //初始化根
			ans=way[i].y;    //初始化sum 
			getroot(x,0);    //找连通块的根 
			solve(way[i].y); //递归处理下一个连通块
		} 
} 

int main()
{
	build();          //建树 
	sum=f[0]=n;       //初始化sum 
	root=0;           //初始化根结点 
	getroot(1,0);     //找重心 
	solve(root);      //点分治 
}

例题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值