点分树点分治
Posted goto_1600
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点分树点分治相关的知识,希望对你有一定的参考价值。
点分树就是按照点分治的顺序建树,他是可修改的,注意这个树维护不是原来的父子关系,有可能会跨层,所以我们千万不要去想他的父子关系,因为你会发现并不对应。。 这个树会特别胖,也就是层数特别低,并且它的好处是能不重不漏地刚好维护任意两点之间的距离关系,我们可以使用划分的思想,任意两点的距离刚好对于他们的祖先,而他们祖先并不多,最多只有log(n)个,然后再套个数据结构例如权值线段树,可以干很多事情,建树过程的话就是每次找子树的重心当根,然后不断迭代。。。写这种算法唯一需要注意的就是代码别抄错,然后暴力所有点当根的贡献,虽然看起来可能n^2,但如果用分治的眼光去看的话,总共log层,每一层操作的节点数最多为n,所以并不多。
例题
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct node
int l;
int r;
int minv;
void node_init()
minv=2e9;
tr[N*200];
bool st[N];
int idx;
int a[N];
int root[N];
vector<pair<int,int>>v[N];
vector<pair<int,int>>father[N];
void insert(int &u,int l,int r,int k,int dist)
if(!u)
u=++idx;
tr[u].minv=2e9;
tr[u].minv=min(tr[u].minv,dist);
if(l==r)
return ;
int mid=l+r>>1;
if(k<=mid)
insert(tr[u].l,l,mid,k,dist);
else
insert(tr[u].r,mid+1,r,k,dist);
int query(int u,int l,int r,int ql,int qr)
if(!u) return 2e9;
if(l>=ql && r<=qr) return tr[u].minv;
int mid=l+r>>1;
int res=2e9;
if(ql<=mid) res=min(res,query(tr[u].l,l,mid,ql,qr));
if(mid<qr) res=min(res,query(tr[u].r,mid+1,r,ql,qr));
return res;
int get_size(int u,int fa)
if(st[u]) return 0;
int sum=1;
for(auto jj:v[u])
int j=jj.first;
int w=jj.second;
if(j==fa) continue;
sum+=get_size(j,u);
return sum;
int get_wc(int u,int fa,int tot,int &wc)
if(st[u]) return 0;
int sum=1;
int maxv=0;
for(auto jj:v[u])
int j=jj.first;
int w=jj.second;
if(j==fa) continue;
int sz=get_wc(j,u,tot,wc);
maxv=max(maxv,sz);
sum+=sz;
if(max(tot-sum,maxv)<=tot/2)
wc=u;
return sum;
void build_apple_tree(int u,int fa,int rt,int dist)
if(st[u]) return;
insert(root[rt],1,10000,a[u],dist);
father[u].push_back(rt,dist);
for(auto jj:v[u])
int j=jj.first;
int w=jj.second;
if(j==fa) continue;
build_apple_tree(j,u,rt,dist+w);
void dfs(int u,int fa)
if(st[u]) return ;
int sz=get_size(u,fa);
get_wc(u,fa,sz,u);
build_apple_tree(u,fa,u,0);
st[u]=true;
for(auto jj:v[u])
int j=jj.first;
int w=jj.second;
if(j==fa) continue;
dfs(j,u);
void solve()
tr[0].minv=2e9;
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=0;i<n-1;i++)
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
v[a].push_back(b,c);
v[b].push_back(a,c);
dfs(1,-1);
while(m--)
int op;
scanf("%d",&op);
if(op==1)
int u,x;
scanf("%d%d",&u,&x);
for(auto jj:father[u])
int j=jj.first;
int w=jj.second;
insert(root[j],1,10000,x,w);
else
int u,x,y;
scanf("%d%d%d",&u,&x,&y);
int res=2e9;
for(auto jj:father[u])
int j=jj.first;
int w=jj.second;
res=min(res,w+query(root[j],1,10000,x,y));
if(res==2e9)
printf("-1\\n");
continue;
res*=2;
printf("%d\\n",res);
signed main()
solve();
以上是关于点分树点分治的主要内容,如果未能解决你的问题,请参考以下文章