点分治总结

概念

点分治可以:

  • 处理树上路径问题(通常与“树上任意两点之间的路径”有关)(如树上距离、树上路径边数等)
  • 处理树上可二分型问题:使用点分治优化一步步走的过程。(如树的重心)

点分治通过不断找树的重心并删除,划分成若干个子树,在子树内再找重心继续递归。每个子树内分别求解答案。

复杂度为O(nlogn)(还取决于calc函数的复杂度,calc函数的复杂度最大为O(szlogsz)

代码分析

注:以P4178 Tree为例(求出树上两点距离小于等于k的有序点对数量)

void ask_rt(int x, int fa) // 找到子树的重心
{
	sz[x] = 1;
	int mx = 0;
	for (int i = hd[x]; i; i = nxt[i])
	{
		int y = to[i];
		if (vis[y] || y == fa) continue; // 注意
		ask_rt(y, x);
		sz[x] += sz[y];
		mx = max(mx, sz[y]);
	}
	mx = max(mx, sum - sz[x]);
	if (mx < mn)
	{
		mn = mx;
		rt = x;
	}
	return;
}

void work(int x, int fa, int s) // 算出以当前子树重心为根x和子树内其他节点y之间的所有距离
{
	sta[ ++ top] = s;
	for (int i = hd[x]; i; i = nxt[i])
	{
		int y = to[i], z = w[i];
		if (vis[y] || y == fa) continue;
		work(y, x, s + z);
	}
	return;
}

int calc(int rt_, int s) // 找到当前子树内的答案
{
	top = 0;
	work(rt_, 0, s);
	sort(sta + 1, sta + top + 1);
	int cnt = 0;
	for (int i = 1; i < top; i ++ )
	{
		int pos = upper_bound(sta + i + 1, sta + top + 1, k - sta[i]) - sta;
		cnt += (pos - i - 1);
	}
	return cnt;
}

void part(int x) // 进行点分治
{
	vis[x] = 1; ask_rt(x, 0); // 重算一遍size
	res += calc(x, 0);
	for (int i = hd[x]; i; i = nxt[i])
	{
		int y = to[i], z = w[i];
		if (vis[y]) continue;
		res -= calc(y, z); // 去重,防止重复计算。因为路径会在calc(x,0)和calc(y,0)中都计算一次
		mn = 1e9, rt = 0, sum = sz[y];
		ask_rt(y, 0), part(rt); // 注意
	}
	return;
}
posted @   andysj  阅读(62)  评论(2编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示