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

省选模拟赛:树

20181231省选模拟赛:树 LCT维护树直径_第1张图片
20181231省选模拟赛:树 LCT维护树直径_第2张图片

分析

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

代码

#include
#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;
}

你可能感兴趣的:(数据结构-平衡树)