「LOJ #2187」「SHOI2014」三叉神经树

Description

Jv0P3D.png

Hint

(1le n, qle 5 imes 10^5)

Solution

当整棵树的某一个叶子结点发生修改时,考虑连带被改变状态的结点分布在那些位置上。

首先,一定在这个叶子结点到根结点的这条链上。其次,改变的结点一定是一段连续的链

对于“ 改变的结点一定是一段连续的链 ” 的解释:设 ( ext{sum}(i)) 为(非叶)结点 (i) 的三个儿子中 (1) 的个数,那么从修改 ((1 ightarrow 0)) 的叶子结点的父结点开始往上,满足 ( ext{sum}(i) = 2) 的结点都要改变,直到某一个结点的 ( ext{sum})( e 2) 了,那么这个节点就不会变(( ext{sum})值还是要改变),于是显然地这个节点的祖先也不会变(( ext{sum}) 值和点的状态都不变)。

既然如此,那我们只要找出这个“断点”,就可以转化为链上加((+1 / -1))的问题了。

两种解法:

  1. 轻重链剖分:时间复杂度 (O(nlog^2 n)),最后一段二分确定断点。
  2. ( exttt{Link-Cut Tree}):时间复杂度 (O(nlog n)),直接维护这个断点。

笔者采用了方法 2.

其实我们可以在 splay 中的结点维护两个子段:以当前结点为根的子树中深度最大的 ( ext{sum} e 1)( e 2) 的结点编号(pos[x][0/1])。

那么 pushup 的时候,顺序是 ( ext{右子结点} ightarrow ext{当前结点} ightarrow ext{左子结点})

inline void pushup(int x) {
	pos[x][0] = pos[rc][0], pos[x][1] = pos[rc][1];
	if (!pos[x][0]) pos[x][0] = (sum[x] != 1) ? x : pos[lc][0];
	if (!pos[x][1]) pos[x][1] = (sum[x] != 2) ? x : pos[lc][1];
}

为什么这样呢?别忘了 辅助树的性质( ext{splay}) 的中序遍历的结点深度递增。 这样就保证了右边的深度大于当前结点的深度,当前结点的深度大于左边的深度。其中 pos 的定义是尽可能深度大。

此外,更新加法标记也很有技巧:( ext{sum}) 值直接加,点的状态根据新的 ( ext{sum}) 重新计算,但是 ( ext{pos}) 如何更新?

答案是……直接交换!可以发现,被修改点一定是 ( ext{sum}) 值上发生了: (1 leftarrow 2)(2 leftarrow 1)。于是本质上就是 swap 了一下。

inline void setAdd(int x, int v) {
	sum[x] += v, val[x] = (sum[x] > 1);
	swap(pos[x][0], pos[x][1]), add[x] += v;
}

其他的……基本就是基础 LCT。对了,这个题不需要换根,即不需要 reverse


接下来是主函数。单独把这个拿出来是因为细节实在是太多了。答案 ( ext{ans}) 单独在主函数中维护。

首先,由于叶结点的特殊性,修改只能从 目标叶子结点的父结点开始 修改,然后单点修改叶子节点。

其次,如果 pos 值为 (0) 说明一路上没有阻挡的结点,直接更新到 (1) 号根节点,并且 ( ext{ans} leftarrow ext{ans} oplus 1)

详见代码:

Code

/*
 * Author : _Wallace_
 * Source : https://www.cnblogs.com/-Wallace-/
 * Problem : LOJ #2187 SHOI2014 三叉神经树
 */
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
const int N = 5e5 + 5;
const int S = N * 3;

int ch[S][2], fa[S];
int add[S], sum[S], val[S];
int pos[S][2];

#define lc ch[x][0]
#define rc ch[x][1]

inline bool isRoot(int x) {
	return x != ch[fa[x]][0] && x != ch[fa[x]][1];
}
inline int getc(int x) {
	return x == ch[fa[x]][1];
}

inline void pushup(int x) {
	pos[x][0] = pos[rc][0], pos[x][1] = pos[rc][1];
	if (!pos[x][0]) pos[x][0] = (sum[x] != 1) ? x : pos[lc][0];
	if (!pos[x][1]) pos[x][1] = (sum[x] != 2) ? x : pos[lc][1];
}
inline void setAdd(int x, int v) {
	sum[x] += v, val[x] = (sum[x] > 1);
	swap(pos[x][0], pos[x][1]), add[x] += v;
}
inline void pushdown(int x) {
	if (!add[x]) return;
	if (lc) setAdd(lc, add[x]);
	if (rc) setAdd(rc, add[x]);
	add[x] = 0;
}
inline void pushdownAll(int x) {
	if (!isRoot(x)) pushdownAll(fa[x]);
	pushdown(x);
}

inline void rotate(int x) {
	int y = fa[x], z = fa[y];
	int k = getc(x), w = ch[x][!k];
	if (!isRoot(y)) ch[z][getc(y)] = x;
	ch[x][!k] = y, ch[y][k] = w;
	if (w) fa[w] = y;
	fa[y] = x, fa[x] = z;
	pushup(y), pushup(x), pushup(z);
}
inline void splay(int x) {
	pushdownAll(x);
	for (register int y = fa[x]; !isRoot(x); rotate(x), y = fa[x])
		if (!isRoot(y)) rotate(getc(x) != getc(y) ? x : y);
	pushup(x);
}

inline void access(int x) {
	for (register int y = 0; x; x = fa[y = x])
		splay(x), rc = y, pushup(x);
}

vector<int> G[S];
int n, q;

void Dfs(int x, int f) {
	sum[x] = 0;
	for (vector<int>::iterator it = G[x].begin(); it != G[x].end(); it++)
		if (f != *it) {
			Dfs(*it, x);
			sum[x] += val[*it];
		}
	if (x <= n) val[x] = (sum[x] > 1);
}

signed main() {
	ios::sync_with_stdio(false);
	
	cin >> n;
	for (register int i = 1; i <= n; i++)
		for (register int j = 0, x; j < 3; j++) {
			cin >> x;
			G[x].push_back(i);
			G[i].push_back(x);
			fa[x] = i;
		}
	for (register int i = n + 1; i <= n * 3 + 1; i++)
		cin >> val[i];
	
	Dfs(1, 0);
	
	int ans = val[1];
	for (cin >> q; q; --q) {
		int last, x, v, w;
		cin >> last, x = fa[last], v = val[last] ? -1 : 1;
		access(x), splay(x), w = pos[x][val[last]];
		
		if (w) {
			splay(w);
			setAdd(ch[w][1], v);
			pushup(ch[w][1]);
			
			sum[w] += v;
			val[w] = (sum[w] > 1);
			pushup(w);
		} else {
			ans ^= 1;
			setAdd(x, v);
			pushup(x);
		}
		val[last] ^= 1;
		cout << ans << '
';
	}
}
原文地址:https://www.cnblogs.com/-Wallace-/p/12818525.html