2019ICPC上海网络赛 - A. Lightning Routing I

题意:

给定一棵有 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值