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的错虽然很难调,但耐心一点,可以增强你的改错技巧~~
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章