20181231省选模拟赛:树 LCT维护树直径

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/85605142

省选模拟赛:树

在这里插入图片描述
在这里插入图片描述

分析

考场上想出来了一个被卡常了的点分治的 O ( n l o g 2 ) O(nlog^2) 的做法,T到起飞。。
有一种假装好些好调的 L C T LCT 做法。
因为 L C T LCT 的辅助树内维护的是一条链,而我们维护的是到这条实链上的最浅和最深的节点到当前子树内的最远距离。
考虑采用 r m x rmx l m x lmx 分别表示最深和最浅, m x s mxs 表示最长链。
既然是到当前子树内,那么肯定要用到子树信息。
首先考虑实链上的转移,假设当前节点是 S p l a y Splay 树中的 p p ,那么 l s ls 表示它在原树上的祖先, r s rs 表示它在原树上的某条子树内的链。以 l m x lmx 的转移为例。
l m x [ l s ] > l m x [ p ] lmx[ls]->lmx[p]
保留子树的答案,剩下只需要考虑过 p p 的路径。
l m x [ r s ] + s u m [ l s ] + a [ p ] > l m x [ p ] lmx[rs]+sum[ls]+a[p]->lmx[p]
沿着实链走下去。
再考虑虚子树的转移。我们需要得到的就是虚子树内深度最大的节点。考虑 l m x [ p ] lmx[p] 的意义,实链上最浅的节点实际上也是原树上子树的根节点。所以 l m x [ p ] lmx[p] 也可以作为子树信息,表示子树内最浅的节点到子树内一点最远距离。
所以可以采用维护子树信息的操作。用一个set来维护。
M i n ( s o n ) + a [ p ] + s u m [ l s ] > l m x [ p ] Min(son)+a[p]+sum[ls]->lmx[p]
走到 p p 后沿着虚子树走下去。
r m x rmx 的转移是类似的,有了 l m x lmx , r m x rmx 的信息, m x s mxs 稍微讨论一下转移即可。
代码与常数均小的一个神仙做法。

代码

#include<bits/stdc++.h>
#define ls ch[p][0]
#define rs ch[p][1]
const int N = 1e5 + 10, inf = 1e9;
using std::max;
typedef long long LL;
typedef std::multiset<LL> MI;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
MI Chain[N], Path[N];
int a[N], ch[N][2], fa[N], pr[N], to[N << 1], nx[N << 1], tp;
LL sum[N], lmx[N], rmx[N], mxs[N], Ans;
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u);}
bool wh(int p) {return ch[fa[p]][1] == p;}
bool Ir(int p) {return ch[fa[p]][0] != p && ch[fa[p]][1] != p;}
void Era(MI &s, int v) {
	if(s.find(v) == s.end()) puts("ERRR");
	s.erase(s.find(v));
}
void Era(int u, int v) {
	Era(Chain[u], lmx[v]); Era(Path[u], mxs[v]);
}
void Ins(int u, int v) {
	Chain[u].insert(lmx[v]); Path[u].insert(mxs[v]); 
}
LL fir(MI &s) {return s.size() ? *s.rbegin() : -inf;}
LL sec(MI &s) {return s.size() > 1 ? *(++s.rbegin()) : -inf;}
void Re(LL &a, LL b) {a = max(a, b);}
void Up(int p) {
	sum[p] = sum[ls] + sum[rs] + a[p];
	LL cha = max(0LL, fir(Chain[p]));
	LL L = max(cha, rmx[ls]) + a[p];
	LL R = max(cha, lmx[rs]) + a[p];
	lmx[p] = max(lmx[ls], sum[ls] + R);
	rmx[p] = max(rmx[rs], sum[rs] + L);
	mxs[p] = max(rmx[ls] + R, lmx[rs] + L);
	Re(mxs[p], max(mxs[ls], mxs[rs]));
	Re(mxs[p], fir(Path[p]));
	Re(mxs[p], cha + sec(Chain[p]) + a[p]);
	Re(mxs[p], cha + a[p]);
}
void Rotate(int p) {
	int f = fa[p], g = fa[f], c = wh(p);
	if(!Ir(f)) ch[g][wh(f)] = p; fa[p] = g;
	ch[f][c] = ch[p][c ^ 1]; if(ch[f][c]) fa[ch[f][c]] = f;
	ch[p][c ^ 1] = f; fa[f] = p; Up(f);
}
void Splay(int p) {
	for(;!Ir(p); Rotate(p))
		if(!Ir(fa[p])) Rotate(wh(fa[p]) == wh(p) ? fa[p] : p);
	Up(p);
}
void Access(int u) {
	for(int p = u, pr = 0; p; pr = p, p = fa[p]) {
		Splay(p);
		if(pr) Era(p, pr);
		if(rs) Ins(p, rs);
		rs = pr; 
		Up(p);		
	}
	Splay(u);
}
void Change(int u, int v) {Access(u); a[u] = v; Up(u); Ans = mxs[u];}
LL Query(int p) {
	Access(p); LL res = 0;
	LL cha = max(0LL, fir(Chain[p]));
	LL L = max(cha, rmx[ls]) + a[p];
	LL R = max(cha, lmx[rs]) + a[p];
	res = max(rmx[ls] + R, lmx[rs] + L);
	Re(res, cha + sec(Chain[p]) + a[p]);
	Re(res, cha + a[p]);
	return res;
}
void Dfs(int u) {
	for(int i = pr[u]; i; i = nx[i])
		if(to[i] != fa[u]) {
			fa[to[i]] = u;
			Dfs(to[i]);
			Ins(u, to[i]);
		}
	Up(u);
}
int main() {
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	int n = ri(), m = ri();
	for(int i = 1;i < n; ++i)
		adds(ri(), ri());
	for(int i = 1;i <= n; ++i)
		a[i] = ri();
	for(int i = 0;i <= n; ++i)
		lmx[i] = rmx[i] = mxs[i] = -inf;
	Dfs(1); Ans = mxs[1];
	for(;m--;) {
		int op = ri(), u;
		if(op == 1) printf("%I64d\n", Ans);
		else if(op == 2) printf("%I64d\n", Query(ri()));
		else u = ri(), Change(u, ri());
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/85605142
今日推荐