点分治

点分治

简介

点分治是一种高效处理树上路径统计问题的分治算法,适用于:

  • 统计满足条件的路径。

  • 求最长/最短路径

  • 求路径权值的第 \(k\) 大/小

  • 其他各类可合并的路径性质统计

算法原理

对于一棵树,所有路径可分为两类:

  • 不经过根节点的路径(完全在某一子树内部)

  • 经过根节点的路径

然后我们可以分别对这两类路径进行统计。

例题选讲

  • CF161D、P4178
板子(桶版本)
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=5e4+5;
int n,k;
int rt,ans,maxp,id,tot;
int siz[N],tmp[N],cnt[N];
bool del[N];
vector<int> G[N];

void get_siz(int cur,int fa){
	siz[cur]=1;
	for(int i:G[cur]){
		if(i==fa||del[i])
			continue;
		get_siz(i,cur);
		siz[cur]+=siz[i];
	}
}
void get_rt(int cur,int fa){
	int mx=-1e18;
	for(int i:G[cur]){
		if(i==fa||del[i])
			continue;
		get_rt(i,cur);
		mx=max(mx,siz[i]);
	}
	mx=max(mx,tot-siz[cur]);
	if(mx<maxp)
		maxp=mx,rt=cur;
}
void get_dis(int cur,int fa,int d){
	if(d>k)
		return;
	tmp[++id]=d;
	for(int i:G[cur]){
		if(i==fa||del[i])
			continue;
		get_dis(i,cur,d+1);
	}
}
void solve(int cur){
	maxp=1e18;
	get_siz(cur,0),get_rt(cur,0);
	tot=siz[cur],cnt[0]=1;
	for(int i:G[rt]){
		if(del[i])
			continue;
		id=0;
		get_dis(i,rt,1);
		for(int i=1;i<=id;i++)
			if(k>=tmp[i])
				ans+=cnt[k-tmp[i]];
		for(int i=1;i<=id;i++)
			cnt[tmp[i]]++;
	}
	for(int i=0;i<=k;i++)
		cnt[i]=0;
	del[rt]=1;
	for(int i:G[rt]){
		if(del[i])
			continue;
		solve(i);
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>k;
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	solve(1);
	cout<<ans;
	return 0;
} 

板子(树状数组版本)
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=5e4+5;
int n,k;
int rt,ans,maxp,id,tot;
int siz[N],tmp[N],cnt[N];
bool del[N];
struct EDGE{
	int v,w;
};
vector<EDGE> G[N];
int tree[N<<2],tag[N<<2];

int lowbit(int x){
    return x&-x;
}
void upd(int x,int y){
	x++;
    for(int i=x;i<=k+1;i+=lowbit(i))
        tree[i]+=y;
}
int qry(int x){
	x++;
    int res=0;
    for(int i=x;i;i-=lowbit(i))
        res+=tree[i];
    return res;
}
void get_siz(int cur,int fa){
	siz[cur]=1;
	for(auto i:G[cur]){
		if(i.v==fa||del[i.v])
			continue;
		get_siz(i.v,cur);
		siz[cur]+=siz[i.v];
	}
}
void get_rt(int cur,int fa){
	int mx=-1e18;
	for(auto i:G[cur]){
		if(i.v==fa||del[i.v])
			continue;
		get_rt(i.v,cur);
		mx=max(mx,siz[i.v]);
	}
	mx=max(mx,tot-siz[cur]);
	if(mx<maxp)
		maxp=mx,rt=cur;
}
void get_dis(int cur,int fa,int d){
	if(d>k)
		return;
	tmp[++id]=d;
	for(auto i:G[cur]){
		if(i.v==fa||del[i.v])
			continue;
		get_dis(i.v,cur,d+i.w);
	}
}
void solve(int cur){
	maxp=1e18;
	get_siz(cur,0),get_rt(cur,0);
	tot=siz[cur],upd(0,1);
	for(auto i:G[rt]){
		if(del[i.v])
			continue;
		id=0;
		get_dis(i.v,rt,i.w);
		for(int j=1;j<=id;j++)
			if(k>=tmp[j])
				ans+=qry(k-tmp[j]);
		for(int j=1;j<=id;j++)
			upd(tmp[j],1);
	}
	for(int i=1;i<=k+1;i++)
		tree[i]=0;
	del[rt]=1;
	int u=rt;
	for(auto i:G[u]){
		if(del[i.v])
			continue;
		solve(i.v);
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	for(int i=1,u,v,w;i<n;i++){
		cin>>u>>v>>w;
		G[u].push_back({v,w});
		G[v].push_back({u,w});
	}
	cin>>k;
	solve(1);
	cout<<ans;
	return 0;
} 
  • CF1575E
posted @ 2026-08-16 22:11  _KidA  阅读(8)  评论(0)    收藏  举报