「POJ3237」Tree【树链剖分+线段树】

题目链接:http://poj.org/problem?id=3237

Tree

Time Limit: 5000MS
Memory Limit: 131072K

Description

You are given a tree with N N N nodes. The tree’s nodes are numbered 1 1 1 through N N N and its edges are numbered 1 through N − 1 N − 1 N1. Each edge is associated with a weight. Then you are to execute a series of instructions on the tree. The instructions can be one of the following forms:

CHANGE i   v i\ v i v Change the weight of the i i i-th edge to v v v
NEGATE a   b a\ b a b Negate the weight of every edge on the path from a a a to b b b
QUERY a   b a\ b a b Find the maximum weight of edges on the path from a a a to b b b

Input

The input contains multiple test cases. The first line of input contains an integer t ( t ≤ 20 ) t (t ≤ 20) t(t20), the number of test cases. Then follow the test cases.

Each test case is preceded by an empty line. The first nonempty line of its contains N ( N ≤ 10 , 000 ) N (N ≤ 10,000) N(N10,000). The next N − 1 N − 1 N1 lines each contains three integers a , b a, b a,b and c c c, describing an edge connecting nodes a a a and b b b with weight c c c. The edges are numbered in the order they appear in the input. Below them are the instructions, each sticking to the specification above. A lines with the word “DONE” ends the test case.

Output

For each “QUERY” instruction, output the result on a separate line.

Sample Input

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

Sample Output

1
3
  • 题意

    给你一颗树,完成如下操作:
    • CHANGE i   v i\ v i v:将输入中的第 i i i条边权值改为 v v v
    • NEGATE a   b a\ b a b:将树上从 a a a b b b的简单路径上所有边权取反(不是变为0
    • QUERY a   b a\ b a b:询问从 a a a b b b的最短路径上的边权最大值
  • 题解 ⇒ \Rightarrow 树链剖分+线段树

    • 锻炼 d e b u g debug debug 好题(除了大佬)
    • 树剖出链,然后线段树维护区间被取反了几次
    • 线段树同时维护区间最大值和最小值,最小值的用处就是当区间被奇数次取反时,区间最大值更新为最小值的负数
    • 而且这题是边权而不是点权,考虑把边权下放到该边所在的链的儿子身上
      在这里插入图片描述
      举个栗子,比如这个图: 1   4 1\ 4 1 4之间的边权 1 1 1放到 4 4 4身上, 4   9 4\ 9 4 9之间的边权 2 2 2放到 9 9 9身上
    • 剩下的就只有实现的细节了(树剖代码这么长,所以还是尽量第一次认真写,不然 h h h hhh hhh d e b u g debug debug到你怀疑 P O J POJ POJ数据错了233 ),由于树剖数组较多,多组数据的情况下记得所有都检查一下是否需要清空

附代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>

using namespace std;
typedef long long ll;
const int maxn=1e5+10;

//存树
struct edge{
	int to,id;
	edge(int a=0,int b=0){
		to=a;id=b;
	}
}son[maxn];
vector<edge> vec[maxn];

//树剖用
int h[maxn],fa[maxn],top[maxn],id[maxn],siz[maxn],rk[maxn],ed[maxn],tot;

//题目数据
int t,n,num,u[maxn],v[maxn],a,b;ll val[maxn],c;
char opt[20];

struct node{
	int l,r,opt;
	ll minn,maxx;
}tree[maxn<<2];

struct segment_tree{
	void build(int le,int ri,int id){
		tree[id].l=le,tree[id].r=ri;tree[id].opt=0;
		if(le==ri) {
			tree[id].minn=tree[id].maxx=val[rk[le]];
			return;
		}
		int mid=(le+ri)>>1;
		build(le,mid,id<<1);
		build(mid+1,ri,id<<1|1);
		pushup(id);
	}

	void pushup(int id){
		tree[id].maxx=max(tree[id<<1].maxx,tree[id<<1|1].maxx);
		tree[id].minn=min(tree[id<<1].minn,tree[id<<1|1].minn);
	}

	void down(int id){
		tree[id<<1].opt=(tree[id<<1].opt+tree[id].opt)%2;
		tree[id<<1|1].opt=(tree[id<<1|1].opt+tree[id].opt)%2;
		ll x=tree[id<<1].minn,y=tree[id<<1].maxx;
		tree[id<<1].minn=-y;tree[id<<1].maxx=-x;
		x=tree[id<<1|1].minn,y=tree[id<<1|1].maxx;
		tree[id<<1|1].minn=-y;tree[id<<1|1].maxx=-x;
		tree[id].opt=0;
	}

	int len(int id){
		return tree[id].r-tree[id].l+1;
	}

	void update(int l,int r,int id){
		if(l<=tree[id].l&&r>=tree[id].r){
			tree[id].opt=(tree[id].opt+1)%2;
			ll a=tree[id].minn,b=tree[id].maxx;
			tree[id].minn=-b;tree[id].maxx=-a;
			return;
		}
		if(tree[id].opt) down(id);
		int mid=(tree[id].l+tree[id].r)>>1;
		if(l<=mid) update(l,r,id<<1);
		if(r>mid) update(l,r,id<<1|1);
		pushup(id);
	}

	ll query(int l,int r,int id){
		ll ans=-1e18;
		if(l<=tree[id].l&&r>=tree[id].r) return tree[id].maxx;

		if(tree[id].opt) down(id);
		int mid=(tree[id].l+tree[id].r)>>1;
		if(l<=mid) ans=max(ans,query(l,r,id<<1));
		if(r>mid) ans=max(ans,query(l,r,id<<1|1));

		pushup(id);
		return ans;
	}

	void modify(int k,int id,ll val){
		if(tree[id].l==tree[id].r) {
			tree[id].minn=tree[id].maxx=val;
			tree[id].opt=0;
			return;
		}

		if(tree[id].opt) down(id);
		int mid=(tree[id].l+tree[id].r)>>1;
		if(k<=mid) modify(k,id<<1,val);
		else modify(k,id<<1|1,val);

		pushup(id);
	}
}data;

struct tree_{
	void init(){
		tot=-1;
		for(int i=1;i<=n;i++) son[i].id=son[i].to=0;
		memset(h,0,sizeof(h));
	}

	void build(){
		dfs1(1,0,1,0);dfs2(1,1,0);
		data.build(1,n-1,1);
	}

	void dfs1(int cur,int fath,int he,int id){ //dfs(root,0,1)
 		h[cur]=he;fa[cur]=fath;siz[cur]=1;
 		if(u[id]!=fath) swap(u[id],v[id]);
 		for(int i=0;i<(int)vec[cur].size();i++){
 			edge nxt=vec[cur][i];
 			if(nxt.to!=fath){
 				dfs1(nxt.to,cur,he+1,nxt.id);
 				siz[cur]+=siz[nxt.to];
 				if(siz[nxt.to]>siz[son[cur].to]) son[cur]=nxt;
 			}
		}
	}


	void dfs2(int cur,int topp,int idd){ //dfs2(root,root)
		id[cur]=++tot;rk[tot]=idd;ed[idd]=tot;top[cur]=topp;
		if(son[cur].to) dfs2(son[cur].to,topp,son[cur].id);
		for(int i=0;i<(int)vec[cur].size();i++){
			edge nxt=vec[cur][i];
			if(nxt.to!=fa[cur]&&nxt.to!=son[cur].to){
				dfs2(nxt.to,nxt.to,nxt.id);
			}
		}
	}

	ll query(int x,int y){
		int topx=top[x],topy=top[y];ll ans=-1e18;
		while(topx!=topy){
			if(h[topx]>=h[topy]){
				ans=max(ans,data.query(id[topx],id[x],1));
				x=fa[topx],topx=top[x];
			}else{
				ans=max(ans,data.query(id[topy],id[y],1));
				y=fa[topy],topy=top[y];
			}
		}
		if(abs(id[x]-id[y])>=1) ans=max(ans,data.query(min(id[x],id[y])+1,max(id[x],id[y]),1));
		return ans;
	}

	void update(int x,int y){
		int topx=top[x],topy=top[y];
		while(topx!=topy){
			if(h[topx]>=h[topy]){
				data.update(id[topx],id[x],1);
				x=fa[topx],topx=top[x];
			}else{
				data.update(id[topy],id[y],1);
				y=fa[topy],topy=top[y];
			}
		}
		if(abs(id[x]-id[y])>=1) data.update(min(id[x],id[y])+1,max(id[x],id[y]),1);
	}

	void modify(int x,ll val){
		data.modify(ed[x],1,val);
	}
}chain;

int main()
{
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);chain.init();
		for(int i=1;i<=n;i++) vec[i].clear();
		for(int i=1;i<n;i++){
			scanf("%d %d %lld",&u[i],&v[i],&val[i]);
			vec[u[i]].push_back(edge(v[i],i));
			vec[v[i]].push_back(edge(u[i],i));
		}
		chain.build();
		while(true){
			scanf("%s",opt+1);
			if(opt[1]=='D') break;

			if(opt[1]=='C') {
				scanf("%d %lld",&a,&c);
				chain.modify(a,c);
			}else if(opt[1]=='N'){
				scanf("%d %d",&a,&b);
				chain.update(a,b);
			}else {
				scanf("%d %d",&a,&b);
				printf("%lld\n",chain.query(a,b));
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值