SPOJ Query on a tree——(樹鏈剖分和LCT動態樹)

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 integersa b c denotes an edge betweena,b of costc (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


題意:給定一棵樹,告訴了每條邊的權值,然後給出兩種操作:

(1)把第i條邊的權值改爲val

(2)詢問a,b路徑上權值最大的邊

轉化爲用邊的孩子節點來表示該邊。



方法一:樹鏈剖分




兩次dfs
第一次dfs就是找重邊,也就是記錄下所有的重邊。

第二次dfs就是連接重邊形成重鏈,具體過程就是:以根節點爲起點,沿着重邊向下拓展,拉成重鏈,不在當前重鏈上的節
點,都以該節點爲起點向下重新拉一條重鏈。

數組作用:

siz[]數組,用來保存以x爲根的子樹節點個數

top[]數組,用來保存當前節點的所在鏈的頂端節點

son[]數組,用來保存重兒子

dep[]數組,用來保存當前節點的深度

fa[]數組,用來保存當前節點的父親

tid[]數組,用來保存樹中每個節點剖分後的新編號

rank[]數組,用來保存當前節點在線段樹中的位置(這題不需要)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define mem(a) memset(a,0,sizeof(a))
#define Size 100000
#define inf 99999999
using namespace std;
struct node{
	int x,y,w;
}tree[Size];
int be[Size<<1],ne[Size<<1],to[Size<<1],w[Size],e;
void add(int x,int y,int z){to[++e]=y;ne[e]=be[x];be[x]=e;w[e]=z;}
int son[Size],fa[Size],size[Size],top[Size];
int deep[Size],tid[Size],cnt;
int a[Size],MAX[Size<<2];
bool p[Size];
void init(){
	mem(be);
	mem(son);
	cnt=e=0;
}
//樹鏈剖分 
void dfs1(int x,int father,int deepth){
	size[x]=1;
	fa[x]=father;
	deep[x]=deepth;
	p[x]=1;
	for(int i=be[x];i;i=ne[i]){
		if(!p[to[i]]){
			dfs1(to[i],x,deepth+1);
			size[x]+=size[to[i]];
			if(!son[x] || size[to[i]]>size[son[x]])//設置重兒子 
				son[x]=to[i];
		}
	}
}
void dfs2(int x,int father){
	top[x]=father;//重鏈中的頂點 
	tid[x]=++cnt;//剖分後點的編號 
	p[x]=1;	
	if(son[x]==0)return;
	dfs2(son[x],father);//先搜重兒子 
	for(int i=be[x];i;i=ne[i]){//搜輕兒子 
		if(!p[to[i]] && to[i]!=son[x]){
			dfs2(to[i],to[i]);//輕鏈中自己爲自己的top 
		}
	}
}
//線段樹
//普通線段樹操作 
void build_tree(int h,int l,int r){
	if(l==r){MAX[h]=a[l];return;}
	int mid=(l+r)>>1;
	build_tree(h<<1,l,mid);
	build_tree(h<<1|1,mid+1,r);
	MAX[h]=max(MAX[h<<1],MAX[h<<1|1]);
}
void update(int h,int l,int r,int x,int w){
	if(l==x && r==x){MAX[h]=w;return;}
	int mid=(l+r)>>1;
	if(x<=mid)update(h<<1,l,mid,x,w);
	else update(h<<1|1,mid+1,r,x,w);
	MAX[h]=max(MAX[h<<1],MAX[h<<1|1]);
}
int Query(int h,int l,int r,int s,int e){
	if(l==s && r==e){return MAX[h];}
	int mid=(l+r)>>1;
	if(e<=mid)return Query(h<<1,l,mid,s,e);
	else if(s>mid)return Query(h<<1|1,mid+1,r,s,e);
	else return max(Query(h<<1,l,mid,s,mid),Query(h<<1|1,mid+1,r,mid+1,e));
}
//將樹鏈剖分轉化爲線段樹 
void change(int n,int x,int y){
	if(deep[tree[x].x]>deep[tree[x].y])//較深的點表示的邊 
		update(1,2,n,tid[tree[x].x],y);
	else
		update(1,2,n,tid[tree[x].y],y);
}
int query(int n,int x,int y){
	int Max=-inf;
	while(top[x]!=top[y]){//不斷轉化到一條重鏈上 
		if(deep[top[x]]<deep[top[y]])swap(x,y);//重鏈頂點的深度比較 
		Max=max(Max,Query(1,2,n,tid[top[x]],tid[x]));
		x=fa[top[x]];
	}
	if(deep[x]<deep[y])swap(x,y);
	if(x!=y)Max=max(Max,Query(1,2,n,tid[y]+1,tid[x]));//要+1,變到下一個邊 
	return Max;
}
int main(){
	int Case;
	scanf("%d",&Case);
	while(Case--){
		init();
		int n;
		scanf("%d",&n);
		for(int i=1;i<n;i++){
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			tree[i].x=x;tree[i].y=y;tree[i].w=z;
			add(x,y,z);add(y,x,z);
		}
		mem(p);
		dfs1(1,1,1);
		mem(p);
		dfs2(1,1);
		for(int i=1;i<n;i++){
			if(deep[tree[i].x]>deep[tree[i].y])//較深的點表示連接的邊 
				a[tid[tree[i].x]]=tree[i].w;
			else
				a[tid[tree[i].y]]=tree[i].w;
		}
		build_tree(1,2,n);
		while(1){
			static char ch[10];
			static int x,y;
			scanf("%s",&ch);
			if(ch[0]=='D')break;
			scanf("%d%d",&x,&y);
			if(ch[0]=='C')
				change(n,x,y);
			else
				printf("%d\n",query(n,x,y));
		}
	}
	return 0;
}

方法二:LCT

先將將邊與點相連,使得邊在樹中,方便操作,更改時直接更改邊即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(x) tree[x].fa
#define LC(x) tree[x].child[0]
#define RC(x) tree[x].child[1]
#define REV(x) tree[x].rev
#define Size 300010
using namespace std;
inline int read(){
	int sum=0,fg=1;char c=getchar();
	while(c<'0' || c>'9'){if(c=='-')fg=-1;c=getchar();}
	while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
	return sum*fg;
}
struct lct{
	int fa,child[2],rev,add;
	int v,MAX;
}tree[Size];
int be[Size],ne[Size],to[Size],e;
struct link_cut_tree{
	inline bool isroot(int x){
		return LC(F(x))!=x && RC(F(x))!=x;
	}
	inline void pushup(int x){
		tree[x].MAX=max(tree[LC(x)].MAX,tree[RC(x)].MAX);
		tree[x].MAX=max(tree[x].MAX,tree[x].v);
	}
	inline void pushdown(int x){
		if(REV(x)){
			REV(x)^=1;REV(LC(x))^=1;REV(RC(x))^=1;
			swap(LC(x),RC(x));
		}
	}
	void Pushdown(int x){
		if(!isroot(x))Pushdown(F(x));
		pushdown(x);
	}
	inline void rotate(int x){
		int A=F(x),B=F(A);bool w=(RC(A)==x);
		if(!isroot(A)){
			if(LC(B)==A)LC(B)=x;
			else if(RC(B)==A)RC(B)=x;
		}
		F(tree[x].child[w^1])=A;F(A)=x;F(x)=B;
		tree[A].child[w]=tree[x].child[w^1];tree[x].child[w^1]=A;
		pushup(A);pushup(x);
	}
	inline void splay(int x){
		Pushdown(x);
		while(!isroot(x)){
			if(!isroot(F(x)))rotate(x);
			rotate(x);
		}
	}
	inline void access(int x){
		for(int i=0;x;i=x,x=F(x))splay(x),RC(x)=i,pushup(x);
	}
	inline int find_root(int x){
		access(x);splay(x);
		while(LC(x))x=LC(x);
		return x;
	}
	inline void reverse(int x){
		access(x);splay(x);REV(x)^=1;
	}
	inline void link(int x,int y,int k){
		reverse(x);F(x)=y;tree[y].v=k;
	}
	inline void cut(int x,int y){
		reverse(x);access(y);splay(y);
		F(LC(y))=0;LC(y)=0;
	}
	inline void update(int x,int w){
		access(x);splay(x);tree[x].v=w;
		pushup(x);
	}
	inline int query(int x,int y){
		reverse(x);access(y);splay(y);
		return tree[y].MAX;
	}
}LCT;
void add(int x,int y){to[++e]=y;ne[e]=be[x];be[x]=e;}
void dfs(int x,int fa){
	for(int i=be[x];i;i=ne[i]){
		int v=to[i];
		if(v!=fa){
			tree[v].fa=x;
			dfs(v,x);
		}
	}
}
void init(){
	memset(be,0,sizeof(be));e=0;
	memset(tree,0,sizeof(tree));
}
struct node{
	int x,y,v;
}a[Size];
int main(){
	int Case=read();
	while(Case--){
		init();
		int n=read();
		for(int i=1;i<n;i++){
			int x=read(),y=read(),val=read();
			a[i].x=x,a[i].y=y;a[i].v=val;
			add(i+n,x);add(x,i+n);add(i+n,y);add(y,i+n);
		}
		char tp[10];
		dfs(1,1);
		for(int i=1;i<n;i++)
			LCT.update(i+n,a[i].v);
		while(1){
			scanf("%s",tp);
			if(tp[0]=='D')break;
			else if(tp[0]=='C'){
				int x=read(),val=read();
				LCT.update(x+n,val);
			}
			else{
				int x=read(),y=read();
				printf("%d\n",LCT.query(x,y));
			}
		}
	}
	return 0;
}


發佈了83 篇原創文章 · 獲贊 27 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章