动态树直径
1. 改变一个点的可选状态——捉迷藏
1.1
https://www.cnblogs.com/LCat90/p/18284512。
1.2
线段树暴力合并直径左右端点。由于 dis 里面有 lca,所以考虑 rmq 求 lca,复杂度单 log。
代码:
void pushup(int p) {
int a = T[p << 1].x, b = T[p <<1].y, c = T[rs].x, d = T[rs].y;
T[p].ans = max({T[ls].ans, T[rs].ans, dis(a, c), dis(a, d), dis(b, c), dis(b, d)});
int j = T[p].ans;
if(j == T[ls].ans) T[p] = T[ls];
else if(j == T[rs].ans) T[p] = T[rs];
else if(j == dis(a, c)) T[p].x = a, T[p].y = c;
else if(j == dis(a, d)) T[p].x = a, T[p].y = d;
else if(j == dis(b, c)) T[p].x = b, T[p].y = c;
else if(j == dis(b, d)) T[p].x = b, T[p].y = d;
}
2. 改变边权——[CEOI2019] Dynamic Diameter
2.1 欧拉序
实际上是求区间最大的 \(\large dis_x+dis_y-2dis_{lca(x,y)}\)。
然后用欧拉序把 \(lca(x,y)\) 替换成区间 RMQ 即可。设 \(a_i=dis_i\)。
则算:\(\large a_x+a_y-\min\limits _{i\in [pre_x, pre_y]} 2a_i\) 即可。
再加上 \(x,y\) 的范围:\(\large \max\limits _{x=1,y=x}^{2n-1}( a_x+a_y-\min\limits _{i\in [pre_x, pre_y]} 2a_i)\)。
然后他说:可以线段树维护?????
维护左右子区间的信息,最后 pushup 合并即可。细节不阐述,确实比较好想。
但是有一个问题,如何每次维护并更新 \(a_x\) 的值?
2.1.1 根据性质子树区间加
首先可以动态在 seg 上维护:每次将子树的 \(a_i\) 加上 \(val\) 即可。
然后子树区间,根据欧拉序的性质,找到 \(fir_x,dfn_x\) 即可。注意改的是一条边更下面的点的子树。
2.1.2 树状数组
不知道别人在干什么。不管。
一遍过样例!!!虽然环境极其艰苦 hhh……代码:
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N = 4e5 + 5; int read();
int n, q, w, a[N], dfn[N], cnt, sum;
int px[N], py[N], pz[N];
struct node {
int ans, maxn, minn; // ax 的 maxmin
int lmax, rmax; // 前缀 ax - 2 * ai,\
// !!!!!!!注意:l 的意思是 ax 是 l,那么就要在右边找 minn
int add;
} T[N << 2];
// 设 ans = L + R - M
void pushup(int p) {
T[p].maxn = max(T[p << 1].maxn, T[p << 1 | 1].maxn);
T[p].minn = min(T[p << 1].minn, T[p << 1 | 1].minn);
T[p].lmax = max({T[p << 1].lmax, T[p << 1 | 1].lmax, T[p << 1].maxn - 2 * T[p << 1 | 1].minn});
T[p].rmax = max({T[p << 1].rmax, T[p << 1 | 1].rmax, T[p << 1 | 1].maxn - 2 * T[p << 1].minn});
T[p].ans = max({T[p << 1].ans, T[p <<1 | 1].ans, T[p << 1].lmax + T[p << 1 | 1].maxn, T[p << 1].maxn + T[p << 1 | 1].rmax});
}
void upd(int p, int x) {
T[p].add += x, T[p].maxn += x, T[p].minn += x;
T[p].lmax -= x, T[p].rmax -= x;
}
void pushdown(int p) { // 区间的 a[x] += add
if(T[p].add) {
upd(p << 1, T[p].add);
upd(p << 1 | 1, T[p].add);
T[p].add = 0;
}
}
void update(int p, int ql, int qr, int x, const int l = 1, const int r = cnt) {
if(ql <= l and r <= qr) return upd(p, x), void();
int mid = l + r >> 1; pushdown(p);
if(ql <= mid) update(p << 1, ql, qr, x, l, mid);
if(qr > mid) update(p << 1 | 1, ql, qr, x, mid + 1, r);
pushup(p);
}
struct sb { int x, y; }; vector <sb> G[N];
int fir[N], dep[N], bl[N];
void dfs(int x, int fa) {
dfn[x] = fir[x] = ++cnt; dep[x] = dep[fa] + 1;
bl[cnt] = x;
for(auto it : G[x]) {
int to = it.x;
if(to == fa) continue ;
dfs(to, x);
dfn[x] = ++cnt; bl[cnt] = x;
}
}
signed main() {
cin >> n >> q >> w;
for(int i = 1, x, y, z;i < n; ++i) {
x = read(), y = read(), z = read();
G[x].pb((sb){y, z}), G[y].pb((sb){x, z});
px[i] = x, py[i] = y, pz[i] = z;
}
dfs(1, 0);
for(int i = 1;i < n; ++i) { // init
int x = px[i], y = py[i], z = pz[i];
if(dep[x] < dep[y]) swap(x, y);
update(1, fir[x], dfn[x], z);
}
int lst = 0;
while(q--) {
int x = read(), y = read();
x = (x + lst) % (n - 1) + 1, y = (y + lst) % w;
int s1 = px[x], s2 = py[x];
if(dep[s1] < dep[s2]) swap(s1, s2);
update(1, fir[s1], dfn[s1], -pz[x]);
update(1, fir[s1], dfn[s1], pz[x] = y);
printf("%lld\n", lst = T[1].ans);
}
return 0;
}
/*
修改每个点的 dis
*/
int read() {
char c; int f = 1, sum = 0;
while(c < '0' or c > '9') {if(c == '-') f = -1;c = getchar();}
while(c >= '0' and c <= '9') {sum = (sum << 3) + (sum << 1) + (c ^ 48);c = getchar();}
return sum * f;
}