spoj 375 Query on a tree——(樹鏈剖分orLCT動態樹)

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路徑上權值最大的邊

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

當然樹鏈剖分我就不詳細說了,具體可以看ACdreamer的博客

上一發代碼

具體思路:就是把邊用點來表示,然後線段樹維護更新

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 100010
using namespace std;
char s[10010];
int Begin[maxn],size[maxn],to[maxn],tid[maxn],Next[maxn],Rank[maxn],cnt,e,count;
int top[maxn],deep[maxn],fa[maxn],w[maxn];
struct node{
	int l,r,w;
};
node a[maxn];
bool p[maxn];
int tree[maxn],num[maxn],son[maxn];
void add(int x,int y,int z){
	to[++e] = y;
	Next[e] = Begin[x];
	Begin[x] = e;
	w[e] = z;
}
void dfs(int h){
	p[h]=1;
	size[h] = 1;
	for(int  i = Begin [h] ; i ; i = Next[i]){
		if(!p[to[i]]){
			fa[to[i]]=h;
			deep[to[i]]=deep[h]+1;
			dfs(to[i]);
			size[h] += size[to[i]];
			if(son[h] == 0 || size[to[i]] > size[son[h]]){
				son[h] = to[i];
			}
		}
	}
}
void bfs(int h,int father){
	p[h]=1;
	top[h] = father;
	tid[h] = ++cnt;
	if(son[h] == 0){
		return;
	}
	bfs(son[h],father);
	for(int I = Begin[h];I;I=Next[I]){
		if(!p[to[I]]){
			bfs(to[I],to[I]);
		}
	} 
}
void jianshu(int h,int l,int r){
	if(l== r){
		tree[h] = num[l];
		return;
	}
	int mid = (l+r)/2;
	jianshu(h*2,l,mid);
	jianshu(h*2+1,mid+1,r);
	tree[h] = max(tree[h*2],tree[h*2+1]);
}
int question(int h,int l,int r,int s,int e){
	if(l == s && r == e){
		return tree[h];
	}
	int mid=(l+r)/2;
	if(e<=mid)return question(h*2,l,mid,s,e);
	else if(s>mid)return question(h*2+1,mid+1,r,s,e);
	else{
		return max(question(h*2,l,mid,s,mid),question(h*2+1,mid+1,r,mid+1,e));
	}
}
void change(int h,int l,int r,int th,int k){
	if(l==r){
		tree[h] = k;
		return;
	}
	int mid = (l+r)/2;
	if(th<=mid){
		change(h*2,l,mid,th,k);
	}
	else change(h*2+1,mid+1,r,th,k);
	tree[h] = max(tree[h*2],tree[h*2+1]);
}
int query(int x,int y,int m){
	int ans = -99999999;
	while(top[x] != top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		ans = max(ans,question(1,2,m,tid[top[x]],tid[x]));
		x = fa[top[x]];
	}
	if(x==y)return ans;
	if(deep[x]<deep[y])swap(x,y);
	ans = max(ans,question(1,2,m,tid[y]+1,tid[x]));
	return ans;
}
int main(){
	int i,j,k,m,n,_,x,y,z;
	scanf("%d",&_);
	while(_--){
		memset(Begin,0,sizeof(Begin));
		memset(son,0,sizeof(son));
		cnt=0;
		e=0;
		scanf("%d",&m);
		for(i=1;i<m;i++){
			scanf("%d",&a[i].l);scanf("%d",&a[i].r);scanf("%d",&a[i].w);
			add(a[i].l,a[i].r,a[i].w);
			add(a[i].r,a[i].l,a[i].w);
		}
		deep[1] = 1;
		memset(p,0,sizeof(p));
		dfs(1);
		memset(p,0,sizeof(p));
		bfs(1,1);
		for(i=1;i<m;i++){
			if(deep[a[i].l]>deep[a[i].r]){
				num[tid[a[i].l]] = a[i].w;
			}
			else num[tid[a[i].r]] = a[i].w;
		}
		jianshu(1,2,m);
		while(1){
			scanf("%s",&s);
			if(s[0] == 'D'){
				break;
			}
			scanf("%d%d",&x,&y);
			if(s[0] == 'Q'){
				printf("%d\n",query(x,y,m));
			}
			else{
				if(deep[a[x].l]>deep[a[x].r])
				change(1,2,m,tid[a[x].l],y);
				else change(1,2,m,tid[a[x].r],y);
			}
		}
	}
	return 0;
}
第二種方法:LCT

雖然時間是慢了點,但可以當做練LCT的改邊權的模板來做:

具體思路:把新建一個點,是這個點在邊的兩個端點之間,然後表示這條邊的權值:

具體操作可以看看代碼,pushup也和平常LCT裏點修改的操作相同

#include<cmath> 
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 100010
using namespace std;
int to[maxn],Begin[maxn],Next[maxn],e;
void add(int x,int y){
	to[++e] = y;
	Next[e] =Begin[x];
	Begin[x] =e;
}
char s[10010];
bool p[maxn];
struct node{
	int fa,child[2],rev,val,maxx;
};
node a[maxn];
int val[maxn];
bool isroot(int h){
	if(a[a[h].fa].child[0]!=h&&a[a[h].fa].child[1]!=h)return 1;
	return 0;
}
void pushup(int h){
	a[h].maxx = max(a[a[h].child[0]].maxx,a[a[h].child[1]].maxx);
	a[h].maxx = max(a[h].maxx,a[h].val);
}
void pushdown(int h){
	if(a[h].rev){
		a[h].rev^=1;
		a[a[h].child[0]].rev^=1;
		a[a[h].child[1]].rev^=1;
		swap(a[h].child[0],a[h].child[1]);
	}
}
void PUSHDOWN(int h){
	if(!isroot(h))PUSHDOWN(a[h].fa);
	pushdown(h);
}
void rotate(int h){
	int A=a[h].fa,B=a[A].fa;
	int w=0;
	if(a[A].child[1]==h)w=1;
	if(!isroot(A)){
		if(a[B].child[0]==A)a[B].child[0]=h;
		else if(a[B].child[1]==A)a[B].child[1]=h;
	}
	a[h].fa=B;
	a[a[h].child[w^1]].fa=A;
	a[A].fa=h;
	a[A].child[w]=a[h].child[w^1];
	a[h].child[w^1]=A;
	pushup(A);pushup(h);
}
void splay(int h){
	PUSHDOWN(h);
	while(!isroot(h)){
		if(!isroot(a[h].fa))rotate(h);
		rotate(h);
	}
}
void access(int h){
	int y=0;
	while(h){
		splay(h);
		a[h].child[1]=y;
		pushup(h);
		y=h;
		h=a[h].fa;
	}
}
void makeroot(int h){
	access(h);splay(h);a[h].rev^=1;
}
int findroot(int h){
	access(h);
	splay(h);
	while(a[h].child[0]){
		h=a[h].child[0];
	}
	return h;
}
void link(int x,int y){
	makeroot(x);a[x].fa=y;
}
void cut(int x,int y){
	makeroot(x);access(y);splay(y);
	a[x].fa=a[y].child[0]=0;
}
void dfs(int h){
	p[h] = 1;
	for(int i = Begin[h];i;i = Next[i]){
		if(!p[to[i]]){
			a[to[i]].fa=h;
			dfs(to[i]);
		}
	}
}
void updada(int x,int y){
	access(x);
	splay(x);
	a[x].val = y;
	pushup(x);
}
int main(){
	int i,j,k,m,n;
	int _;
	scanf("%d",&_);
	while(_--){
		memset(Begin,0,sizeof(Begin));
		e=0;
		memset(p,0,sizeof(p));
		memset(a,0,sizeof(a));
		memset(val,0,sizeof(val));
		int x,y,z;
		scanf("%d",&m);
		for(i=1;i<m;i++){
			scanf("%d%d%d",&x,&y,&z);
			val[i] = z;
			add(x,i+m),add(i+m,x),add(i+m,y),add(y,i+m);
		}
		dfs(1);
		for(i=1;i<=m;i++){
			updada(i+m,val[i]);
		}
		while(1){
			scanf("%s",s);
			if(s[0]=='D')break;
			int x,y;
			scanf("%d%d",&x,&y);
			if(s[0]=='Q'){
				makeroot(x);
				access(y);
				splay(y);
				printf("%d\n",a[y].maxx);
			}
			else{
				updada(x+m,y);
			}
		}
	}
	return 0;
}

lct的錯雖然很難調,但耐心一點,可以增強你的改錯技巧~~
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章