点分治小记

前言

点分治适合处理大规模的树上路径信息问题。

例题:Tree

假设我们要对根为 rtrt 的树进行求解。会有任意一条合法的路径,都可以被分成两类:

  1. 路径包含 rt 的。而这种路径可以看作 ArtBA\to rt \to B
  2. 不包含 rt 的。

对于情况 1。设定 sis_i 为经过 rtrt 的距离和为 0i0\sim i 的路径个数,也就是 arta\to rt 部分。我们可以枚举其子树,算出所有 rtBrt \to B。假设一条路径距离为 xx。那对答案的贡献就是 skxs_{k-x},再将当前子树到 rtrt 的路径加入到 sis_i

这样来看,一次统计是 O(n)O(n) 的。接下来算情况 2。其实是很简单的。可以将 rtrt 的子树的某一点看作新的根,将 rtrt“删除”就可以了。

但是时间复杂度并没有加快很多。此处引入树的重心,定义:对于一个无根树,指定 ii 为根。设 fif_iii 的子树中,节点个数最多的子树的节点个数,其实就是 fi=max{sizev}f_i = \max\{size_v\}。如果 fx=minfif_x = \min{f_i},那么 xx 为树的重心。树的重心有一个性质:fxn/2f_x\leq n/2

假如我们每次都取树的重心为新的根,那么新的树最多有 n/2n/2 个节点。所以最多递归 logn\log n 次。因此复杂度为 nlognn\log n注意:选新的根后,需要重新计算子树大小来选重心,否则复杂度错误。


代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+10;
struct EDGE{int v,nxt,w;}e[N*2];
int h[N],idx;
void ae(int x,int y,int w) {e[++idx] = {y,h[x],w};h[x] = idx;}
int n,ans,k;

struct BIT{
	int c[N],t[N];
	int tg,len=N;
	void clear() {tg++;}
	void init(int l) {len=l;}
	#define lowbit(x) ((x)&-(x))
	void upd(int x,int k) {for(;x<=len;x+=lowbit(x)) if(tg==t[x]) c[x]+=k; else c[x]=k, t[x]=tg;}
	int s(int x) {int p=0;for(;x;x-=lowbit(x)) if(tg==t[x]) p+=c[x]; return p;}
}c;

int vis[N],root,rnum,sum;// 注意维护 root:重心,rnum:f_root sum:n 
// 如果 vis_x == 1.那么认定该节点被删除。 
int sz[N];
void GRoot(int u,int fa) { // 求根
	/*
	虽然树的重心在定义上来说,是以每一个点为根的。但是也可以指定一个为根。
	第 i 个时候算出 i 的子树的值与子树的父亲的值。 
	*/ 
	sz[u] = 1;
	int ssz = 0;// son size
	for(int i = h[u];i;i=e[i].nxt) {
		int w = e[i].w, v = e[i].v;
		if(v == fa || vis[v]) continue;
		GRoot(v,u);
		sz[u] += sz[v];
		ssz = max(ssz ,sz[v]);
	}
	if(ssz < sum - sz[u]) ssz = sum - sz[u]; 
	if(ssz < rnum) rnum = ssz, root = u; 
}

int q[N], cnt;

void dfs(int u,int fa,int d) {
	if(d<=k) q[++cnt] = d;
	for(int i = h[u];i;i = e[i].nxt) {
		int v = e[i].v, w = e[i].w;
		if(v == fa || vis[v]) continue;
		dfs(v,u,d+w);
	}
}
void dfa(int u) {
	vis[u] = 1;
	c.upd(0 + 1,1);// 路径也可以形如 rt->B。不一定是 A->rt->B。
	// + 1 是因为树状数组中 lowbit(0) = 0 而设置的偏移量。 
	for(int i = h[u];i;i = e[i].nxt) {
		int v = e[i].v, w = e[i].w;
		if(vis[v]) continue;
		dfs(v,u,w);
		for(int j=1;j<=cnt;j++) ans += c.s(k-q[j] + 1);
		for(int j=1;j<=cnt;j++) c.upd(q[j] + 1,1);
		cnt = 0;
	}
	c.clear(); // clear BIT
	for(int i = h[u];i;i = e[i].nxt) {
		int v = e[i].v;
		if(vis[v]) continue;
		sum = sz[v], rnum = 1e9;
		GRoot(v,-1), GRoot(root,-1);
		//这两行不要缺漏,多求一遍方便下一次求根时算 sum 
		dfa(root);
	}
}
signed main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n;
	for(int i=1;i<n;i++) {
		int x,y,w;
		cin>>x>>y>>w;
		ae(x,y,w), ae(y,x,w);
	}
	cin>>k;
    c.init(k);
	sum = n; rnum = 1e9;
	GRoot(1,-1), GRoot(root,-1);
	dfa(root);
	cout<<ans;
	return 0;
}

本文作者:cjrqwq

本文链接:https://www.cnblogs.com/yfzqwq/p/18492758

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   cjrqwq  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
展开
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.