P4332 [SHOI2014]三叉神经树
作者:互联网
要求维护一棵树:
- 每个点有 \(3\) 个端口,分为输入端和输出端(连向父亲)。输出端的个数 \(\leq 1\)。
- 如果一个点输入端权值为 \(1\) 的个数 \(\geq 2\),那么这个点的权值为 \(1\),否则为 \(0\)。
- 支持动态修改叶子节点,修改后询问根节点的权值。
\(1 \leq n \leq 5\times 10^5\),时限 \(4\text{s}\),空限 \(250\text{MB}\)。
sol
可以发现,每一次修改叶子节点后,权值改变的点的集合一定是一条自上而下的链,且这条链开头不定,但结尾一定是被修改点。
然后我们还可以发现(此处记 \(sum_i\) 表示 \(i\) 点的三个儿子中,权值为 \(1\) 的点得个数):
- 若被修改点是从 \(1\) 改成 \(0\) 的,那么从被修改点开始向上,只有 \(sum=2\) 的点的颜色改变(从 \(1\) 变成 \(0\))。
- 若被修改点是从 \(0\) 改成 \(1\) 的,那么从被修改点开始向上,只有 \(sum=1\) 的点的颜色改变(从 \(0\) 变成 \(1\))。
那么,求出一个点子树内最深的 \(sum \ne 1\) 或 \(sum \ne 2\) 的点即为修改这个点的子树内的叶子节点的链头。
那么怎么维护?
\(\mathcal O(n \log^3 n)\)
考虑树链剖分,用线段树维护区间最小值和最大值,当且仅当某个区间的最小值和最大值均为 \(1\) 或 \(2\) 时才表示这一链的点的权值均为 \(1\) 或 \(2\)。
至于链头,则可以通过二分或倍增来找,总时间复杂度为 \(\mathcal O(n \log^3 n)\)。
具体地,二分 \(\mathcal O(\log n)\),跳重链 \(\mathcal O(\log n)\),线段树查询 \(\mathcal O(\log n)\)。
\(\mathcal O(n \log^2 n)\)
用 LCT
可以 \(\mathcal O(\log n)\) 实现查询链上最大最小值。
故总时间复杂度优化为 \(\mathcal O(n \log^2 n)\)。
\(\mathcal O(n \log n)\)
考虑更直接的方法,维护一个点的信息。
讨论一下,修改 push_up
和 push_down
函数即可。
总时间复杂度为 \(\mathcal O(n \log n)\)。
许多细节。
具体参考代码。
#include <bits/stdc++.h>
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
inline int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = x * 10 + c - '0', c = getchar();
return x * f;
}
const int _ = 1.5e6 + 10;
struct LCT
{
int f[_], c[_][2], id[_][3], v[_], s[_], st[_];
int lz[_];
inline bool isroot(int x)
{
return c[f[x]][0] == x || c[f[x]][1] == x;
}
inline void push_up(int x)
{
id[x][1] = id[rc][1];
id[x][2] = id[rc][2];
if(!id[x][1])
{
if(s[x] != 1)
id[x][1] = x;
else
id[x][1] = id[lc][1];
}
if(!id[x][2])
{
if(s[x] != 2)
id[x][2] = x;
else
id[x][2] = id[lc][2];
}
}
inline void push_(int t, int x)
{
s[t] += x, v[t] = s[t] > 1;
swap(id[t][1], id[t][2]);
lz[t] += x;
}
inline void push_down(int x)
{
if(lz[x])
{
if(lc)
push_(lc, lz[x]);
if(rc)
push_(rc, lz[x]);
lz[x] = 0;
}
}
inline void rotate(int x)
{
int y = f[x], z = f[y], k = c[y][1] == x, w = c[x][!k];
if(isroot(y))
c[z][c[z][1] == y] = x;
c[x][!k] = y, c[y][k] = w;
if(w)
f[w] = y;
f[y] = x, f[x] = z;
push_up(y);
}
inline void splay(int x)
{
int y = x, z = 0;
st[++z] = y;
while(isroot(y))
st[++z] = y = f[y];
while(z)
push_down(st[z--]);
while(isroot(x))
{
y = f[x], z = f[y];
if(isroot(y))
rotate((c[y][0] == x) ^ (c[z][0] == y) ? x : y);
rotate(x);
}
push_up(x);
}
inline void access(int x)
{
for(int y = 0; x; x = f[y = x])
splay(x), rc = y, push_up(x);
}
} tr;
int n, m;
#define pr printf
int tot, head[_], to[_ << 1], nxt[_ << 1];
void add(int u, int v)
{
to[++tot] = v, nxt[tot] = head[u], head[u] = tot;
}
void dfs(int x, int f)
{
tr.s[x] = 0;
for(int i = head[x], v; i; i = nxt[i])
{
v = to[i];
if(v == f) continue;
dfs(v, x);
tr.s[x] += tr.v[v];
}
if(x <= n)
tr.v[x] = tr.s[x] > 1;
}
signed main()
{
n = read();
int x;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= 3; ++j)
{
x = read();
tr.f[x] = i;
add(x, i), add(i, x);
}
for(int i = n + 1; i <= n * 3 + 1; ++i)
tr.v[i] = read();
dfs(1, 0);
m = read();
int lst, w, addtag, ans = tr.v[1];
while(m--)
{
lst = read(), x = tr.f[lst];
addtag = tr.v[lst] ? -1 : 1;
tr.access(x), tr.splay(x);
w = tr.id[x][tr.v[lst] ? 2 : 1];
if(w)
{
tr.splay(w);
tr.push_(tr.c[w][1], addtag);
tr.push_up(w);
tr.s[w] += addtag, tr.v[w] = tr.s[w] > 1;
tr.push_up(w);
}
else
{
ans ^= 1;
tr.push_(x, addtag);
tr.push_up(x);
}
tr.v[lst] ^= 1;
pr("%d\n", ans);
}
return 0;
}
标签:log,修改,int,up,push,三叉神经,SHOI2014,mathcal,P4332 来源: https://www.cnblogs.com/orzz/p/16099327.html