题意:
给定一棵有 n 个结点的边权树,给定 m 次操作,①:C,ei,wi,修改 ei 条边权为 wi;②:Q,vi,询问 vi 到其他点的最长距离。(n, m <= 1e5)
链接:
https://nanti.jisuanke.com/t/41398
解题思路:
对于询问操作,最远点一定是树的直径的某个端点,则问题转化为动态维护树直径及端点,以及动态求两点距离。对于后者,先以任意结点为根求出 dis 数组,对于一个修改,对应为子树 dis 增加某个值,可化 dfs 序用树状数组差分来求,此外还需要求 lca 来计算两点距离。
重点就是如何动态维护树直径端点,由于本人不会点分树以及 LCT,暂且用题解所提到的线段树维护方法实现。 类比求 lca 时候的化 RMQ 问题的方法,设根为 r,由于 dis(u, v) = dis(u, r) + dis(v, r) - 2 * dis(lca, r),且边权非负,我们求出树的全 dfs 序,则对应 in[u] 到 out[v] 区间的最小值就是 dis(lca, r),则两点距离转化为 RMQ 问题。根据树直径定义,需要维护序列中任意两点距离的最大值,考虑用线段树区间合并实现。
令 mx 为区间最大值(vm为对应最大值点),mx2为区间 -2dis(x, r) 最大值,val 为区间两点距离最大值(u,v 为对应两点)考虑区间合并时候 val 跨越左右子树的维护,如图:
故需要一个 lmx 表示区间 u -> mx2 情况的最大值(并记录左端点 vl),rmx 表示区间 mx2 -> v 情况的最大值(并记录右端点 vr),与另一边最大值相加取大合并即可。至于修改操作,就是线段树区间覆盖问题,详见代码。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 2e5 + 5;
const int maxm = 5e2 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
vector<pii> G[maxn];
pii ei[maxn]; int wi[maxn];
ll dis[maxn]; int dep[maxn];
int in[maxn], out[maxn], rk[maxn];
int in2[maxn], out2[maxn], fa[maxn][21];
ll c[maxn];
int n, q, tim, tim2;
struct Node{
int u, v, vl, vr, vm;
ll mx, mx2, lmx, rmx, val, add;
} tr[maxn << 2];
void pushUp(int rt){
tr[rt].mx = max(tr[lson].mx, tr[rson].mx);
tr[rt].mx2 = max(tr[lson].mx2, tr[rson].mx2);
tr[rt].lmx = max(max(tr[lson].lmx, tr[rson].lmx), tr[lson].mx + tr[rson].mx2);
tr[rt].rmx = max(max(tr[lson].rmx, tr[rson].rmx), tr[rson].mx + tr[lson].mx2);
tr[rt].val = max(max(tr[lson].val, tr[rson].val), max(tr[lson].lmx + tr[rson].mx, tr[rson].rmx + tr[lson].mx));
tr[rt].vm = tr[rt].mx == tr[lson].mx ? tr[lson].vm : tr[rson].vm;
if(tr[rt].lmx == tr[lson].lmx) tr[rt].vl = tr[lson].vl;
else if(tr[rt].lmx == tr[rson].lmx) tr[rt].vl = tr[rson].vl;
else tr[rt].vl = tr[lson].vm;
if(tr[rt].rmx == tr[lson].rmx) tr[rt].vr = tr[lson].vr;
else if(tr[rt].rmx == tr[rson].rmx) tr[rt].vr = tr[rson].vr;
else tr[rt].vr = tr[rson].vm;
if(tr[rt].val == tr[lson].val) tr[rt].u = tr[lson].u, tr[rt].v = tr[lson].v;
else if(tr[rt].val == tr[rson].val) tr[rt].u = tr[rson].u, tr[rt].v = tr[rson].v;
else if(tr[rt].val == tr[lson].lmx + tr[rson].mx) tr[rt].u = tr[lson].vl, tr[rt].v = tr[rson].vm;
else tr[rt].u = tr[lson].vm, tr[rt].v = tr[rson].vr;
}
void build(int l, int r, int rt){
tr[rt].add = 0;
if(l == r){
ll d = dis[rk[l]];
tr[rt].mx = d, tr[rt].mx2 = -2 * d;
tr[rt].lmx = tr[rt].rmx = -d;
tr[rt].val = 0;
tr[rt].u = tr[rt].v = rk[l];
tr[rt].vl = tr[rt].vr = tr[rt].vm = rk[l];
return;
}
int mid = gmid;
build(l, mid, lson);
build(mid + 1, r, rson);
pushUp(rt);
}
void pushDown2(int rt, int son){
ll d = tr[rt].add;
tr[son].add += d;
tr[son].mx += d, tr[son].mx2 -= 2 * d;
tr[son].lmx -= d, tr[son].rmx -= d;
}
void pushDown(int rt){
if(tr[rt].add){
pushDown2(rt, lson);
pushDown2(rt, rson);
tr[rt].add = 0;
}
}
void update(int l, int r, int rt, int L, int R, ll val){
if(l >= L && r <= R){
tr[0].add = val;
pushDown2(0, rt);
return;
}
int mid = gmid;
pushDown(rt);
if(L <= mid) update(l, mid, lson, L, R, val);
if(R > mid) update(mid + 1, r, rson, L, R, val);
pushUp(rt);
}
void dfs(int u, int f){
in2[u] = ++tim2, in[u] = ++tim, rk[tim] = u;
fa[u][0] = f, dep[u] = dep[f] + 1;
for(int i = 1; i <= 20; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int i = 0; i < sz(G[u]); ++i){
int w = G[u][i].first, v = G[u][i].second;
if(v == f) continue;
dis[v] = dis[u] + w;
dfs(v, u);
rk[++tim] = u;
}
out[u] = tim, out2[u] = tim2;
}
int lca(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
for(int i = 20; i >= 0; --i){
if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];
}
if(u == v) return u;
for(int i = 20; i >= 0; --i){
if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
}
return fa[u][0];
}
#define lowb(x) ((x)&(-x))
void Update(int x, ll val){
while(x <= n) c[x] += val, x += lowb(x);
}
ll Query(int x){
ll ret = 0;
while(x) ret += c[x], x -= lowb(x);
return ret;
}
ll cal(int u, int v){
int lc = lca(u, v);
ll a = Query(in2[u]), b = Query(in2[v]), c = Query(in2[lc]);
return a + b - 2 * c;
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
scanf("%d", &n);
for(int i = 1; i < n; ++i){
int u, v, w; scanf("%d%d%d", &u, &v, &w);
G[u].pb({w, v}), G[v].pb({w, u});
ei[i] = { u, v }; wi[i] = w;
}
dfs(1, 0);
build(1, tim, 1);
for(int i = 1; i <= n; ++i){
Update(in2[i], dis[i]);
Update(in2[i] + 1, -dis[i]);
}
scanf("%d", &q);
while(q--){
char opt[2]; int x, y; scanf("%s%d", opt, &x);
if(opt[0] == 'C'){
scanf("%d", &y);
int u = ei[x].first, v = ei[x].second;
u = dep[u] > dep[v] ? u : v;
update(1, tim, 1, in[u], out[u], y - wi[x]);
Update(in2[u], y - wi[x]);
Update(out2[u] + 1, wi[x] - y);
wi[x] = y;
}
else{
int u = tr[1].u, v = tr[1].v;
ll ret = max(cal(x, u), cal(x, v));
printf("%lld\n", ret);
}
}
return 0;
}