点分树点分治

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();


以上是关于点分树点分治的主要内容,如果未能解决你的问题,请参考以下文章

#4707. 点分治

bzoj4372 烁烁的游戏

点分治&点分树 复习

[持续更新] 杂谈点分治 点分树 边分治 链分治

分治动态点分治 ([ZJOI2007]捉迷藏)

P3241 [HNOI2015]开店