[SHOI2014]三叉神经树(LCT)
题目:洛谷P4332、LOJ#2187
题目描述:
给你一棵以\(1\)为根,含有\(n\)个非叶子节点和\(2n + 1\)个叶子节点的三叉树,每个非叶子节点都有\(3\)个儿子
所有点的权值只能是\(0\)或\(1\),给定每个叶子节点的权值,,一个非叶子节点的权值与它\(3\)个儿子节点的权值有关,当它\(3\)个儿子节点中\(0\)的个数大于\(1\)的个数,这个节点权值就是\(0\),否则是\(1\)
有\(q\)次操作,每次将一个儿子节点取反,你需要在取反后输出\(1\)号节点的权值
\(n \leq 5\cdot 10^{5}\), \(q \leq 5\cdot 10^{5}\)
蒟蒻题解:
可以发现,每次修改后,一定是修改从下往上连续一段,不妨设\(v_{x}\)表示\(x\)节点的\(3\)个儿子权值和,那么当\(v_{x} \leq 1\)时,\(x\)节点权值为\(0\),当\(v_{x} > 1\)时,\(x\)节点权值为\(1\),为了更方便一些,可以假设叶子节点的\(3\)个儿子权值分别为\(0\)、\(1\)和它本身,也用\(v_{x}\)表示,叶子节点的\(v_{x}\)只能为\(1\)或\(2\)
那么修改的一定是从要修改的那个叶子节点往上连续一段\(v_{x}\)一样的,再加上一个不一样的
方法一:很容易想到树链剖分+线段树维护,每次修改的是一段链,所以可以先求出\(dfs\)序,然后树剖去修改,查询连续一段\(1\)或\(2\),时间复杂度是\(\Theta(nlog^{2} n)\)的,但是因为懒在做\(lct\)专题,所以便没有打了,
方法二:我原本想着用\(lct\)维护一段一样的,放在同一个\(splay\)中,但是怎么搞都没有搞出来,后面在机房神仙\(ljr\)的提点下,才知道问题可以转换为找第一个和叶子节点不相同的点
所以可以开一个数组\(nt[x][0/1]\),表示在\(lct\)当前\(splay\)上以\(x\)为根的子树内深度最大的不为\(1/2\)的位置
上传时,\(nt\)先从右儿子得来,如果没有右儿子或者说右儿子没有不为\(1/2\)的,那么再考虑自身,最后再考虑左儿子,如下:
inline void push_up(int x)
{
nt[x][0] = nt[x][1] = 0;
if (son[x][1]) nt[x][0] = nt[son[x][1]][0], nt[x][1] = nt[son[x][1]][1];
if (!nt[x][0] && val[x] ^ 1) nt[x][0] = x;
if (!nt[x][1] && val[x] ^ 2) nt[x][1] = x;
if (!nt[x][0] && son[x][0]) nt[x][0] = nt[son[x][0]][0];
if (!nt[x][1] && son[x][0]) nt[x][1] = nt[son[x][0]][1];
}
修改时,找到第一个不为\(1/2\)的点,进行单点修改,然后它的右子树内所有点的权值都和当前要修改的叶子节点权值相等,就直接整棵子树全部\(+1\)或者\(-1\),又由于你要下传首先要满足的就是你的子树内全都是\(1\)或者\(2\),\(1\)的话全\(+1\),\(2\)的话全\(-1\),相当于是\(1\)和\(2\)互换了,所以可以如下方式下传(\(ad\)表示的是懒标记,\(val\)表示的是权值):
inline void calc(int x, int y)
{
ad[x] += y, val[x] += y, nt[x][0] ^= nt[x][1] ^= nt[x][0] ^= nt[x][1];
}
inline void push_down(int x)
{
if (son[x][0]) calc(son[x][0], ad[x]);
if (son[x][1]) calc(son[x][1], ad[x]);
ad[x] = 0;
}
我感觉这一题难点只要是把维护一段相同的转换为找最后一个不同的,代码实现不会太难,而本蒟蒻刚开始没想到只要\(1\)和\(2\)互换就行了,所以开了\(yt[x][0/1/2/3]\)表示\(x\)子树内深度最大的权值为\(0/1/2/3\),不知道为什么爆零了,改成上述说的才过了
操作都是一些\(lct\)的操作,时间复杂度是\(\Theta(nlog\ n)\)的
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
const int N = 500005;
const int M = 1500005;
int n, m, l = 1, r, res, g[N], d[N], tr[N][3], q[M], fa[M], val[M], ad[N], son[N][2], nt[N][2];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline void push_up(int x)
{
nt[x][0] = nt[x][1] = 0;
if (son[x][1]) nt[x][0] = nt[son[x][1]][0], nt[x][1] = nt[son[x][1]][1];
if (!nt[x][0] && val[x] ^ 1) nt[x][0] = x;
if (!nt[x][1] && val[x] ^ 2) nt[x][1] = x;
if (!nt[x][0] && son[x][0]) nt[x][0] = nt[son[x][0]][0];
if (!nt[x][1] && son[x][0]) nt[x][1] = nt[son[x][0]][1];
}
inline void calc(int x, int y)
{
ad[x] += y, val[x] += y, nt[x][0] ^= nt[x][1] ^= nt[x][0] ^= nt[x][1];
}
inline void push_down(int x)
{
if (son[x][0]) calc(son[x][0], ad[x]);
if (son[x][1]) calc(son[x][1], ad[x]);
ad[x] = 0;
}
inline bool check(int x)
{
return son[fa[x]][0] == x || son[fa[x]][1] == x;
}
inline void rotate(int x)
{
int y = fa[x], z = fa[y];
bool t = son[y][1] ^ x;
if (check(y)) son[z][son[z][1] == y] = x;
fa[x] = z, fa[y] = x;
if (son[x][t]) fa[son[x][t]] = y;
son[y][t ^ 1] = son[x][t], son[x][t] = y;
push_up(y), push_up(x);
}
inline void splay(int x)
{
g[res = 1] = x;
while (check(g[res])) g[res + 1] = fa[g[res]], ++res;
while (res)
{
if (ad[g[res]]) push_down(g[res]);
--res;
}
while (check(x))
{
int y = fa[x];
if (check(y)) rotate(((son[fa[y]][0] == y) ^ (son[y][0] == x)) ? x : y);
rotate(x);
}
}
inline void access(int x)
{
for (Re i = 0; x; x = fa[i = x])
splay(x), son[x][1] = i, push_up(x);
}
int main()
{
n = read();
for (Re i = 1; i <= n; ++i)
for (Re j = 0; j < 3; ++j) tr[i][j] = read(), fa[tr[i][j]] = i;
for (Re i = n + 1; i <= 3 * n + 1; ++i) val[i] = read() + 1, q[++r] = i;
while (l <= r)
{
int x = q[l++];
if (!fa[x]) break;
if (val[x] > 1) ++val[fa[x]];
++d[fa[x]];
if (d[fa[x]] == 3) q[++r] = fa[x];
}
for (Re i = 1; i <= n; ++i) push_up(i);
m = read();
while (m--)
{
int u = read(), v = fa[u];
access(v), splay(v);
int w = nt[v][val[u] - 1];
if (!w)
if (val[u] == 1) calc(v, 1);
else calc(v, -1);
else
{
splay(w);
if (val[u] == 1) ++val[w], calc(son[w][1], 1);
else --val[w], calc(son[w][1], -1);
push_up(w);
}
val[u] ^= 3;
splay(1);
putchar(48 + (val[1] >> 1)), putchar('\n');
}
return 0;
}

浙公网安备 33010602011771号