动态树直径

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;
}
posted @ 2024-07-23 13:16  LCat90  阅读(2)  评论(0编辑  收藏  举报