SPOJ QTREE Query on a tree

思路:树链剖分的一种,将边权赋值给这条边的儿子节点,(u,v)也就是v节点,u->v,然后线段树维护节点之间的关系。

注意的是,如果u,v在同一天链中的话且dep[u] < dep[v],应该query的是[Seg[u] + 1,Seg[v]]的值,如果不是的话就是这个点到链顶的最大了。

// #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
using namespace std;
// #define DEBUG
#ifdef DEBUG
#define debug(...) printf( __VA_ARGS__ )
#else
#define debug(...)
#endif
#define CLR(x) memset(x, 0,sizeof x)
#define MEM(x,y) memset(x, y,sizeof x)
#define pk push_back
template<class T> inline T Get_Max(const T&a,const T&b){return a < b?b:a;}
template<class T> inline T Get_Min(const T&a,const T&b){return a < b?a:b;}
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> ii;
const double eps = 1e-10;
const int inf = 1 << 30;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 10010;
int head[maxn], pnt[maxn<<2], cost[maxn<<2], nxt[maxn<<2], ecnt;
int fa[maxn], size[maxn], A[maxn], top[maxn], SegId[maxn], TreeId[maxn], son[maxn], dep[maxn];
int Times;
void AddEdge(int u,int v,int c){
	cost[ecnt] = c;pnt[ecnt] = v;nxt[ecnt] = head[u];head[u] = ecnt++;
	cost[ecnt] = c;pnt[ecnt] = u;nxt[ecnt] = head[v];head[v] = ecnt++;
}
int DFS1(int u,int pre,int deep){
	dep[u] = deep;fa[u] = pre;size[u] = 1;
	int tmp = 0;
	for (int i = head[u];i != -1;i = nxt[i]){
		int v = pnt[i];
		if (v==pre) continue;
		A[v] = cost[i];
		int ans = DFS1(v, u,deep + 1);
		size[u] += ans;
		if (son[u] == -1 || ans > tmp){
			tmp = ans;
			son[u] = v;
		}
	}
	return size[u];
}
void DFS2(int u,int header){
	top[u] = header;
	if (son[u] != -1){
		SegId[u] = ++Times;
		TreeId[Times] = u;
		DFS2(son[u], header);
	}else if (son[u] == -1){
		SegId[u] = ++Times;
		TreeId[Times] = u;
		return ;
	}
	for (int i = head[u];i != -1;i = nxt[i]){
		int v = pnt[i];
		if (v!=fa[u]&&v!=son[u])
			DFS2(v,v);
	}
}
int Max[maxn << 2];
void up(int rt){
	Max[rt] = max(Max[rt<<1],Max[rt<<1|1]);
}
void Build(int rt,int L,int R){
	Max[rt] = -inf;
	if (L == R){
		Max[rt] = A[TreeId[L]];
		return ;
	}
	int mid = (L + R)/2;
	Build(rt<<1,L,mid);
	Build(rt<<1|1,mid+1,R);
	up(rt);
}
int QueryMax(int l,int r,int rt,int L,int R){
	if (l <= L && R <= r){
		return Max[rt];
	}
	int mid = (L+R)/2;
	int ans=-inf;
	if (l<=mid) ans = max(ans,QueryMax(l,r,rt<<1,L,mid));
	if (r>mid) ans = max(ans,QueryMax(l,r,rt<<1|1,mid+1,R));
	return ans;
}
int FindMax(int u,int v){
	int f1 = top[u],f2 = top[v];
	int ans = -inf;
	while(f1 != f2){
		if (dep[f1] < dep[f2]){
			swap(f1,f2);
			swap(u,v);
		}
		ans = max(ans,QueryMax(SegId[f1],SegId[u],1,1,Times));
		u = fa[f1];
		f1=top[u];
	}
	if (dep[u] < dep[v]){
		swap(u,v);
	}
	if (u != v) ans = max(ans,QueryMax(SegId[v]+1,SegId[u],1,1,Times));
	return ans;
}
void updata(int pos,int val,int rt,int L,int R){
	if (L==R && R==pos){
		Max[rt]=val;
		return ;
	}
	int mid=(L+R)/2;
	if(pos<=mid) updata(pos,val,rt<<1,L,mid);
	else updata(pos,val,rt<<1|1,mid+1,R);
	up(rt);
}
void Change(int pos,int val){
	int u = pnt[pos], v = pnt[pos^1];
	if(fa[u]==v)swap(u,v);
	updata(SegId[v],val,1,1,Times);
}
int wocao[maxn<<2];
int main()
{	
	// freopen("in.txt","r",stdin);
	// freopen("out.txt","w",stdout);
	int t,n;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		Times = ecnt = 0;
		memset(head, -1,sizeof head);
		for (int i = 1;i < n;++i){
			int u,v,c;
			scanf("%d%d%d",&u,&v,&c);
			AddEdge(u,v,c);
			wocao[i] = head[u];
		}
		memset(son, -1,sizeof son);
		DFS1(1, -1, 1);
		A[1] = -inf;
		DFS2(1, 1);
		Build(1,1,Times);
		char op[10];
		int x,y;
		while(true){
			scanf("%s",op);
			if (op[0] == 'D') break;
			scanf("%d%d",&x,&y);
			if (op[0] == 'Q')
				printf("%d\n",FindMax(x,y));
			else Change(wocao[x],y);
		}
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值