SPOJ Problem Set (classical)375. Query on a treeProblem code: QTREE |
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
题意:一棵包含N 个结点的树,每条边都有一个权值,要求模拟两种操作:(1)改变某条边的权值,(2)询问U,V 之间的路径中权值最大的边。
思路:对树进行轻重边路径剖分。对于询问操作,我们可以分别处理两个点到其最近公共祖先的路径。路径可以分解成最多O(log N)条轻边和O(log N)条重路径,那么只需考虑如何维护这两种对象。对于轻边,我们直接处理即可。而对于重路径,我们只需用线段树来维护。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 10005
#define inf 0x3fffffff
using namespace std;
struct node
{
int u,v,w,next;
}bian[N*2];
struct line
{
int x,y,w;
}ed[N];
int e,id;
//********树链部分**************
//siz[u]u的子节点个数
//top[u]u所在链顶点
//father[u]表示u的父节点
//son[u]与u在同重链上的儿子节点
//ti[u]表示u与其父亲节点的连边,在线段树中的位置
int sz[N],dep[N],son[N],father[N],head[N],ti[N],top[N];
struct p
{
int x,y,w;
}a[N*3];
int max(int a,int b)
{
return a>b?a:b;
}
void add(int u,int v,int w)
{
bian[e].u=u;
bian[e].v=v;
bian[e].w=w;
bian[e].next=head[u];
head[u]=e++;
}
void dfs1(int u,int fa)
{
int i,v;
sz[u]=1; dep[u]=dep[fa]+1; son[u]=0; father[u]=fa;
for(i=head[u];i!=-1;i=bian[i].next)
{
v=bian[i].v;
if(v==fa) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v])
son[u]=v;
}
}
void dfs2(int u,int fa)
{
int i,v;
ti[u]=id++;
top[u]=fa;
if(son[u]!=0)
dfs2(son[u],fa);//标记重边
for(i=head[u];i!=-1;i=bian[i].next)
{//标记轻边
v=bian[i].v;
if(v==father[u]||v==son[u])
continue;
dfs2(v,v);//该链的顶点就是改点
}
}
void build(int t,int x,int y)
{
a[t].x=x; a[t].y=y; a[t].w=-inf;
if(x==y)
return;
int mid=(x+y)>>1,temp=t<<1;
build(temp,x,mid);
build(temp+1,mid+1,y);
}
void update(int t ,int x,int w)
{
if(a[t].x==x&&a[t].y==x)
{
a[t].w=w;
return ;
}
int mid=(a[t].x+a[t].y)>>1,temp=t<<1;
if(x<=mid)
update(temp,x,w);
else
update(temp+1,x,w);
a[t].w=max(a[temp].w,a[temp+1].w);
}
int query(int x,int y,int t)
{
if(a[t].x==x&&a[t].y==y)
return a[t].w;
int mid=(a[t].x+a[t].y)>>1,temp=t<<1;
if(y<=mid)
return query(x,y,temp);
else if(x>mid)
return query(x,y,temp+1);
else
return max(query(x,mid,temp),query(mid+1,y,temp+1));
}
int lca(int x,int y)
{
int ans=-inf;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
swap(x,y);
//x到所在链的顶点所有边的最大值
ans=max(ans,query(ti[top[x]],ti[x],1));
x=father[top[x]];//x所在链的顶点的父节点,转到另一条链上
}
if(dep[x]>dep[y])
swap(x,y);
if(x!=y)//ti[x]指的是x与其父亲的边,所以+1
ans=max(ans,query(ti[x]+1,ti[y],1));
return ans;
}
int main()
{
int t,i,n,x,y;
char str[100];
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
e=0;
memset(head,-1,sizeof(head));
for(i=1;i<n;i++)
{
scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].w);
add(ed[i].x,ed[i].y,ed[i].w);
add(ed[i].y,ed[i].x,ed[i].w);
}
dep[1]=0; id=1; sz[0]=0;
dfs1(1,1);
dfs2(1,1);
//for(i=1;i<=n;i++)
//printf("i=%d sz=%d top=%d father=%d son=%d ti=%d\n",i,sz[i],top[i],father[i],son[i],ti[i]);
build(1,2,n);
for(i=1;i<n;i++)
{
if(dep[ed[i].x]<dep[ed[i].y])
swap(ed[i].x,ed[i].y);
update(1,ti[ed[i].x],ed[i].w);
}
while(scanf("%s",str),strcmp(str,"DONE")!=0)
{
if(str[0]=='Q')
{
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
else
{
scanf("%d%d",&x,&y);
update(1,ti[ed[x].x],y);
}
}
printf("\n");
}
return 0;
}