题解 P6018 [Ynoi2010] Fusion tree

发现每个点距离为 \(1\) 的节点就是儿子或者父亲,因此可以把儿子和父亲分开来算。

计算父亲是很容易的,直接维护 \(a\) 的值。对于操作 \(1\) ,在父亲上标记就行了,表示这个点进行过几次的操作 \(1\)。

对于每个节点维护儿子,就会发现是要维护:单点加入、单点删除、全局 \(+1\),全局异或和。

如果对 01-trie 比较熟悉,可以发现 01-trie 可以维护这个东西。从低位到高位建立 01-trie,大致是要维护每个节点的子树的异或和 \(val\),还有每个节点到父亲的边权 \(w\)(指这条边被经过了几次)。

由于是异或,会发现可以只考虑 \(w\) 的奇偶性。于是 push_up 大致是这样的:

inline void push_up(int u)
{
	w[u]=0,val[u]=0;
	if (nxt[u][0])
	{
		w[u]^=w[nxt[u][0]];
		val[u]^=(val[nxt[u][0]]<<1);
	}
	if (nxt[u][1])
	{
		w[u]^=w[nxt[u][1]];
		val[u]^=(val[nxt[u][1]]<<1);
		if (w[nxt[u][1]]==1) val[u]^=1;
	}
}

注意到只考虑 \(w\) 的奇偶性,因此 insert 和 delete 本质相同。你可以理解为,先加入一个数 \(x\),要把它删除等同于再加一个 \(x\),这样异或和为 \(0\) 了。所以其实没有必要写两个函数(但是好像基本上题解都写了)。

如何处理全局 \(+1\)?从低到高考虑,每一位 \(1\) 变成 \(0\) 同时进位,\(0\) 变成 \(1\) 不进位。递归做就行了。

void add(int x)
{
	swap(nxt[x][0],nxt[x][1]);
	if (nxt[x][0]) add(nxt[x][0]);
	push_up(x);
}

于是就可以维护了。注意一些实现的细节。

提交记录代码实现

上一篇:mac使用VMware Fusion配置ubuntu18.04实录


下一篇:如何无效化缓存-分布式缓存问题