点分树、点分治

点分树就是按照点分治的顺序建树,他是可修改的,注意这个树维护不是原来的父子关系,有可能会跨层,所以我们千万不要去想他的父子关系,因为你会发现并不对应。。 这个树会特别胖,也就是层数特别低,并且它的好处是能不重不漏地刚好维护任意两点之间的距离关系,我们可以使用划分的思想,任意两点的距离刚好对于他们的祖先,而他们祖先并不多,最多只有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();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值