Spoj 375 QTREE(树链剖分)

18 篇文章 0 订阅
3 篇文章 0 订阅

Description

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1.

We will ask you to perfrom some instructions of the following form:

  • CHANGE i ti : change the cost of the i-th edge to ti
    or
  • QUERY a b : ask for the maximum edge cost on the path from node a to node b

Input

The first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow.

For each test case:

  • In the first line there is an integer N (N <= 10000),
  • In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of cost c (c <= 1000000),
  • The next lines contain instructions "CHANGE i ti" or "QUERY a b",
  • The end of each test case is signified by the string "DONE".

There is one blank line between successive tests.

Output

For each "QUERY" operation, write one integer representing its result.

Example

Input:
1

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE

Output:
1
3



分析:第一次敲树链剖分,参考了网上的模板。

 
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
#define INF 0x3f3f3f3f
#define N 100005
using namespace std;
int dep[N],siz[N],fa[N],id[N],son[N],val[N],top[N],fid[N];
//深度,儿子数量,父亲,新编号,重儿子,权值,所属链头,反编号。 
int T,n,num;
vector <int> G[N];
struct Edge
{
	int x,y,val;
} edge[N];
struct Segtree
{
	int l,r,val,down;
} tree[4*N];
void dfs1(int u,int f,int d)
{
	dep[u] = d;
	siz[u] = 1;
	son[u] = 0;
	fa[u] = f;
	for(int v : G[u])
	 if(v != f)
	 {
	 	 dfs1(v,u,d+1);
	 	 siz[u] += siz[v];
	 	 if(siz[son[u]] < siz[v]) son[u] = v;
	 }
}
void dfs2(int u,int tp)
{
	top[u] = tp;
	id[u] = ++num;
	fid[id[u]] = u;
	if(son[u]) dfs2(son[u],tp);
	for(int v : G[u])
	 if(v != fa[u] && v != son[u]) dfs2(v,v);
}
void build(int i,int l,int r)
{
	tree[i].l=l;
	tree[i].r=r;
	tree[i].down = 0;
	if(l == r) 
	{
		tree[i].val = val[fid[l]];
		return;
	}
	int mid=(l+r)>>1;
	build(2*i,l,mid);
	build(2*i+1,mid+1,r);
	tree[i].val = max(tree[2*i].val,tree[2*i+1].val);
}
void Insert(int i,int x,int Val)
{
	int l = tree[i].l,r = tree[i].r;
	if(l == r)
	{
		tree[i].val = Val;
		return;
	}
	int mid = (l+r)/2;
	if(x <= mid) Insert(2*i,x,Val);
	else Insert(2*i+1,x,Val);
	tree[i].val = max(tree[2*i].val,tree[2*i+1].val);
}
int query(int i,int x,int y)
{
	int l = tree[i].l,r = tree[i].r;
	if(l == x && r == y)
	{
		return tree[i].val;
	}
	int mid = (l+r)/2;
	if(y <= mid) return query(2*i,x,y);
	else 
	 if(x <= mid) return max(query(2*i,x,mid),query(2*i+1,mid+1,y));
	 else return query(2*i+1,x,y);
}
int Yougth(int u,int v)
{
	int tp1 = top[u],tp2 = top[v];
	int ans = 0;
	while(tp1 != tp2)
	{
		if(dep[tp1] < dep[tp2])
		{
			swap(tp1,tp2);
			swap(u,v);
		}
		ans = max(query(1,id[tp1],id[u]),ans);
		u = fa[tp1];
		tp1 = top[u]; 
	}
	if(u == v) return ans;
	if(dep[u] > dep[v]) swap(u,v);
	ans = max(query(1,id[son[u]],id[v]),ans);
	return ans;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i = 1;i <= n;i++) G[i].clear();
		for(int i = 1;i < n;i++)
		{
			scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].val);
			G[edge[i].x].push_back(edge[i].y);
			G[edge[i].y].push_back(edge[i].x);
		}
		num = 0;
		dfs1(1,0,1);
		dfs2(1,1);
		for(int i = 1;i < n;i++)
		 if(dep[edge[i].x] > dep[edge[i].y]) val[edge[i].x] = edge[i].val;
		 else val[edge[i].y] = edge[i].val;
		build(1,1,num);
		char s[20];
		while(~scanf("%s",s) && s[0] != 'D')
		{
			int x,y;
			scanf("%d%d",&x,&y);
			if(s[0] == 'Q') printf("%d\n",Yougth(x,y));
			else 
			{
				if(dep[edge[x].x] > dep[edge[x].y]) Insert(1,id[edge[x].x],y);
				else Insert(1,id[edge[x].y],y);
			}
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]中的描述,p1168问题使用了线段树解法。在构造树的过程中,需要遍历整棵树,所以时间复杂度为O(n)。但是在改变一个元素的值时,时间复杂度只有O(log(n))。求和的时候,树的节点表示一个索引范围内元素值之和,只要将区间分割对应上,平均时间复杂度是O(log(n)),最坏情况下不会超过O(n*log(n))。\[1\] 根据引用\[2\]中的描述,QUERY 5 12应该是第4、5条边的极大值。\[2\] 根据引用\[3\]中的描述,代码中的if(L<=MID)和else if(R>MID)的判断条件是为了确保查询范围在左子树或右子树中。如果加上else,会导致错误。\[3\] #### 引用[.reference_title] - *1* [leetCode307:线段树解法](https://blog.csdn.net/cyd1999/article/details/123963164)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Spoj 375 Qtree 树链剖分 + 线段树 解法](https://blog.csdn.net/niuox/article/details/8145842)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值