线段树+树链剖分裸题 spoj 375 Query on a tree

本文介绍了一种解决树形结构中路径查询问题的方法——树链剖分,并结合线段树进行高效的权值更新与查询。通过两次DFS预处理得到关键信息,实现快速查找两点间最大权值边。

 

题目大意:在一颗数上,每条边都有一个权值,让你修改其中一条边的值或者让你找出u,v两点之间权值最大的边。

 

树链剖分,第一个dfs求出每个点的siz(该点拥有的子节点数),son(其儿子节点中siz最大的那个),fa(该节点的父亲节点),dep(该节点所在层数)

第二个dfs就是为按重链为每一条边标记num下标和标记重链。

接着就是用线段树管理这些边,记得这些边是用num下标标记的,所以管理的时候就是管理id数组。

 

更新权值很简单,就是线段树点更新,找到要更新的边的下标然后更新就好了。

找u,v之间的最大权值边,不断的向上更替top,如果他们在一条链上那么直接就是线段树找区间最大值。

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
struct eo{
	int x,y,val;
	void read(){
		scanf("%d%d%d",&x,&y,&val);
	} 
}e[maxn];

int siz[maxn],dep[maxn],fa[maxn],top[maxn];
int son[maxn],id[maxn],val[maxn],t[maxn<<2],num;

vector<int> v[maxn];

void dfs1(int u,int f,int de){
	siz[u]=1;
	fa[u]=f;
	dep[u]=de;
	son[u]=0;
	int max_son=-1;
	for(int i=0;i<v[u].size();i++){
		int temp=v[u][i];
		if(temp==f) continue;
		dfs1(temp,u,de+1);
		siz[u]+=siz[temp];
		
		if(siz[temp]>max_son){
			max_son=siz[temp];
			son[u]=temp;
		}
	}
}

void dfs2(int u,int flag){
	
	id[u]=++num;
	top[u]=flag;
	if(son[u]) dfs2(son[u],flag);
	for(int i=0;i<v[u].size();i++){
		int temp=v[u][i];
		if(temp==son[u]||temp==fa[u]) continue;
		dfs2(temp,temp);
		
	}
} 

void pushup(int rt){
	t[rt]=max(t[rt<<1],t[rt<<1|1]);
	return;
}

void bulid(int l,int r,int rt){
	if(l==r){
		t[rt]=val[l];
		return;
	}
	int mid=(l+r)/2;
	bulid(l,mid,rt<<1);
	bulid(mid+1,r,rt<<1|1);
	pushup(rt);
}

void update(int l,int r,int ad,int c,int rt){
	if(l==ad&&ad==r){
		t[rt]=c;
		return;
	}
	int mid=(l+r)/2;
	if(ad<=mid) update(l,mid,ad,c,rt<<1);
	else update(mid+1,r,ad,c,rt<<1|1);
	pushup(rt);
}

int query(int l,int r,int L,int R,int rt){
	if(l>=L&&r<=R){
		return t[rt];
	}
	int mid=(l+r)/2;
	int ret=0;
	if(L<=mid) ret=max(ret,query(l,mid,L,R,rt<<1));
	if(R>mid) ret=max(ret,query(mid+1,r,L,R,rt<<1|1));
	return ret;
}

int cha(int x,int y){
	int topx=top[x],topy=top[y],ans=0;
	while(topx!=topy){
		if(dep[topx]<dep[topy]){
			swap(x,y);
			swap(topx,topy);
		}
		ans=max(ans,query(1,num,id[top[x]],id[x],1));
		x=fa[topx];
		topx=top[x];		
	}
	if(x==y) return ans;
	if(dep[x]>dep[y]) swap(x,y);
	ans=max(ans,query(1,num,id[son[x]],id[y],1));
	return ans;
}


int main(){
	int t,n,a,b,c;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<n;i++){
			e[i].read();
			v[e[i].x].push_back(e[i].y);
			v[e[i].y].push_back(e[i].x);						
		}
		num=0;
		dfs1(1,0,1);
		dfs2(1,1);
		for(int i=1;i<n;i++){
			if(dep[e[i].x]<dep[e[i].y]) swap(e[i].x,e[i].y);
			val[id[e[i].x]]=e[i].val;
		}
		
		bulid(1,num,1);
		char s[10];
		int t1,t2;
		while(~scanf("%s",&s)&&s[0]!='D'){
			scanf("%d%d",&t1,&t2);
			if(s[0]=='C'){
				update(1,num,id[e[t1].x],t2,1);
			}
			else if(s[0]=='Q'){
				printf("%d\n",cha(t1,t2));
			}
		} 
		for(int i=1;i<=n;i++){
			v[i].clear();
		}
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值