点分治
点分治
简介
点分治是一种高效处理树上路径统计问题的分治算法,适用于:
-
统计满足条件的路径。
-
求最长/最短路径
-
求路径权值的第 \(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
浙公网安备 33010602011771号