- 题意: 一个有边权树,操作是询问节点\(a到b\)所经过边的最大权和修改某边的边权
- 思路: 树链剖分,将边权下放到儿子的点权即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll long long
#define FOR(i,l,r) for(int i = l ; i <= r ;++i )
#define inf 0x3f3f3f3f
#define EPS (1e-9)
#define ALL(T) T.begin(),T.end()
#define lson(i) i<<1
#define rson(i) (i<<1|1)
using namespace std;
const int maxn =30010;
struct Edge{
int to,next,w;
}edge[maxn*2];
int head[maxn],tot;
int top[maxn]; // 所在重链的顶端节点
int fa[maxn]; // 父亲
int deep[maxn]; // 深度
int num[maxn]; // 儿子个数
int p[maxn]; // 在dfs序的位置
int fp[maxn]; // 位置节点号的反向映射
int son[maxn]; // 重儿子
int pos;
void addedge(int u,int v,int w){
edge[tot].to = v;
edge[tot].next = head[u];
edge[tot].w = w;
head[u] = tot++;
}
void init(){
memset(head,-1,sizeof(head));
memset(son,-1,sizeof(son));
tot = 0;
pos = 1;
}
//第一遍dfs 处理fa,num,deep,son
void dfs1(int u,int pre,int d){
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].to;
if(v!=pre){
dfs1(v,u,d+1);
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]])
son[u] = v;
}
}
}
// 第二遍dfs 处理 top,p,fp
void dfs2(int u,int sp){
top[u] = sp;
p[u] = pos++;
fp[p[u]] = u;
if(son[u]== -1) return ;
dfs2(son[u],sp); // 当前链继续走重儿子
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].to;
if( v!= son[u] && v!=fa[u])
dfs2(v,v); // 以自己为链首的新链
}
}
int a[maxn];
struct node{
int l,r;
int sum,ma;
}seg[maxn*4];
void push_up(int p){
seg[p].ma = max(seg[lson(p)].ma,seg[rson(p)].ma);
}
void build(int pp,int l,int r){
seg[pp].l = l;
seg[pp].r = r;
if(l==r){
// 反向映射到原来的节点
// seg[pp].ma = seg[pp].sum = a[fp[l]];
return;
}
int mid = (l+r)>>1;
build(lson(pp),l,mid);
build(rson(pp),mid+1,r);
// push_up(pp);
}
void update(int p,int l,int r,int val){
if(seg[p].l >= l && seg[p].r <= r){
// seg[p].sum = val;
seg[p].ma = val;
return;
}
int mid = (seg[p].r+seg[p].l)>>1;
if(l<=mid) update(lson(p),l,r,val);
if(r>mid) update(rson(p),l,r,val);
push_up(p);
}
int qmax(int p,int l,int r){
if(seg[p].l >= l && seg[p].r <= r){
return seg[p].ma;
}
int res = -inf;
int mid = (seg[p].r+seg[p].l)>>1;
if(l<=mid) res = max(res,qmax(lson(p),l,r));
if(r>mid) res = max(res,qmax(rson(p),l,r));
return res;
}
int fmax(int u,int v){
int res = -1e9;
int tu = top[u], tv = top[v];
while(tu != tv){
if(deep[tu]< deep[tv]){
swap(tu,tv);
swap(u,v);
}
res=max(res,qmax(1,p[tu],p[u]));
u = fa[tu];
tu = top[u];
}
if(deep[u] > deep[v]) swap(u,v);
res = max(res,qmax(1,p[son[u]],p[v]));
return res;
}
int n;
int main(){
int t;
cin >> t;
while(t--){
scanf("%d",&n);
int fr,to,w;
init();
FOR(i,1,n-1){
scanf("%d%d%d",&fr,&to,&w);
addedge(fr,to,w);
addedge(to,fr,w);
}
dfs1(1,0,0);
dfs2(1,1);
// FOR(i,1,n) {
// scanf("%d",&a[i]);
// }
build(1,1,n);
for(int i=0;i< tot;i+=2){
if(fa[edge[i].to]==edge[i+1].to){
update(1,p[edge[i].to],p[edge[i].to],edge[i].w);
// a[edge[i].to] = edge[i].w;
}else{
update(1,p[edge[i+1].to],p[edge[i+1].to],edge[i+1].w);
// a[edge[i+1].to] = edge[i].w;
}
}
// FOR(i,1,n){
// printf("%d %d %d %d %d %d\n",top[i],fa[i],deep[i],num[i],p[i],son[i]);
// }cout << endl;
char op[10];
while(scanf("%s",op) && op[0]!='D'){
scanf("%d%d",&fr,&to);
if(op[0]=='Q')
if(fr==to) printf("0\n");
else printf("%d\n",fmax(fr,to));
else{
Edge e = edge[fr*2-1];
Edge e2 = edge[fr*2-2];
w = fa[e.to]==e2.to ? e.to : e2.to;
update(1,p[w],p[w],to);
}
}
}
return 0;
}
注意: 当跳到两个节点在一条链时,询问的不是 p[u] 到 p[v]
, 而是p[son[u]] 到 p[v]
因为u节点存的是u到其父亲的边,u到v是不会经过的