「SPOJ 375」QTREE - Query on a tree

Description

题目链接:SPOJ 375

给定一棵 $n$ 个节点的树,边按照输入顺序编号为 $1\sim n-1$,每条边都有一个权值 $c_i$ 需要对这棵树进行若干次操作,操作分为 $2$ 种:

  • CHANGE i t:将第 $i$ 条边的权值 $c_i$ 修改为 $t$
  • QUERY a b:询问从节点 $a$ 到 $b$ 的路径上的边权最大值。

询问以 DONE 结束,有 $T$ 组数据。

数据范围:$T\le 20$,$n\le10^4$,$c_i,t\le 10^6$


Solution

对于这类树的题目,我们首先可以想到用树链剖分维护。而这题需要维护的边的信息,那么我们可以把每条边的信息放在较深的节点上,就转化成了维护点的信息了。

但是这样做需要注意一个问题:每次查询时,$\text{LCA}$ 的节点维护的信息是它上方的边,因此这个点不能被查询到。如何解决?我们根据树剖的性质可以发现以下方法:记 $x$ 的 $\text{DFS}$ 序为 $d_x$,那么最后查询 $u,v$ 的信息时(这里令 $u$ 的深度比 $v$ 浅),我们可以发现 $u$ 一定是 $\text{LCA}$,所以只要查询 $d_u+1,d_v$ 就可以避开 $\text{LCA}$ 了。

时间复杂度:$O(n\log^2 n)$(这里认为操作次数和 $n$ 同阶)


Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson rt<<1
#define rson rt<<1|1
inline char nc() {
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
template <class Tp> inline void read(register Tp &s) {
s=0;
register bool neg=0;
register char c=nc();
for(;c<'0'||c>'9';c=nc()) neg|=(c=='-');
for(;c>='0'&&c<='9';s=s*10+(c^48),c=nc());
s=(neg?-s:s);
}

const int N=1e5+5,M=2e5+5;
int n,idx,dfn[N],seq[N],U[N],V[N],W[N],val[N],fa[N],dep[N],top[N],sz[N],hvy[N];
int tot,lnk[N],ter[M],nxt[M],wei[M],seg[N<<2];

void add(int u,int v,int w) {
ter[++tot]=v,wei[tot]=w,nxt[tot]=lnk[u],lnk[u]=tot;
}
void dfs1(int u,int f) {
fa[u]=f,dep[u]=dep[f]+1,sz[u]=1,hvy[u]=top[u]=0;
for(int i=lnk[u];i;i=nxt[i]) {
int v=ter[i];
if(v==f) continue;
val[v]=wei[i];
dfs1(v,u);
sz[u]+=sz[v];
if(sz[hvy[u]]<sz[v]) hvy[u]=v;
}
}
void dfs2(int u,int tp) {
dfn[u]=++idx,seq[idx]=u,top[u]=tp;
if(!hvy[u]) return;
dfs2(hvy[u],tp);
for(int i=lnk[u];i;i=nxt[i]) {
int v=ter[i];
if(v==fa[u]||v==hvy[u]) continue;
dfs2(v,v);
}
}
void pushup(int rt) {
seg[rt]=std::max(seg[lson],seg[rson]);
}
void build(int rt,int l,int r) {
if(l==r) {
seg[rt]=val[seq[l]];
return;
}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(rt);
}
void modify(int x,int rt,int l,int r,int val) {
if(l==r) {
seg[rt]=val;
return;
}
int mid=(l+r)>>1;
if(x<=mid) modify(x,lson,l,mid,val);
else modify(x,rson,mid+1,r,val);
pushup(rt);
}
int query(int x,int y,int rt,int l,int r) {
if(x>y) return 0;
if(x<=l&&r<=y) return seg[rt];
int mid=(l+r)>>1,res=0;
if(x<=mid) res=std::max(res,query(x,y,lson,l,mid));
if(mid<y) res=std::max(res,query(x,y,rson,mid+1,r));
return res;
}
int chainQuery(int u,int v) {
if(u==v) return 0;
int res=0;
for(int fu=top[u],fv=top[v];fu^fv;u=fa[fu],fu=top[u]) {
if(dep[fu]<dep[fv]) std::swap(u,v),std::swap(fu,fv);
res=std::max(res,query(dfn[fu],dfn[u],1,1,n));
}
if(dep[u]>dep[v]) std::swap(u,v);
res=std::max(res,query(dfn[u]+1,dfn[v],1,1,n));
return res;
}
int main() {
int T;
for(read(T);T--;) {
read(n);
idx=tot=0,memset(lnk,0,sizeof(lnk));
for(int i=1;i<n;++i) {
int u,v,w;
read(u),read(v),read(w);
add(u,v,w),add(v,u,w);
U[i]=u,V[i]=v,W[i]=w;
}
dfs1(1,0),dfs2(1,1),build(1,1,n);
while(1) {
char c=nc();
while(c<'A'||c>'Z') c=nc();
if(c=='D') break;
int x,y;
read(x),read(y);
if(c=='C') {
int u=U[x],v=V[x];
if(fa[v]==u) std::swap(u,v);
modify(dfn[u],1,1,n,y);
} else {
printf("%d\n",chainQuery(x,y));
}
}
}
return 0;
}
0%