P9067 [Ynoi Easy Round 2022] 虚空处刑 题解
谁告诉你 Ynoi 就要手写数据结构了?
维护 map<int, list<int>> C[N]
,$C_{i,j}$ 表示与 $i$ 点所在连通块相邻的 $j$ 色连通块序列。
$a_x\gets y$ 时,直接对 $C_x$ 和 $C_i|i\in C_{x,y}$ 启发式合并,然后发现 $\forall v$ 与 $x$ 相邻,要更新 $C_{v,y}$,假了。
lxl 说过,这种邻域信息维护父亲一定死,所以令 $C_{i,j}$ 表示在 $i$ 点所在连通块下方的 $j$ 色连通块序列。
$a_x\gets y$ 时,直接对 $C_x$ 和 $C_i|i\in C_{x,y}$ 启发式合并,然后发现只需要更新 $C_{\text{fa}_x,y}$ 了。
这里没必要从 $C_{\text{fa}_x,a_x}$ 里删掉 $x$,启发式合并的时候判断一下是不是真的要合并就行了。
unordered_map
跑不过 map
。
#include <map>
#include <list>
#include <cstdio>
using namespace std;
struct E
{
int v, t;
} e[1000050];
map<int, list<int>> C[1000050];
int n, m, c, a[1000050], s[1000050], f[1000050], g[1000050], h[1000050];
void A(int u, int v)
{
e[++c] = {v, h[u]};
h[u] = c;
}
int F(int x) { return x == f[x] ? x : f[x] = F(f[x]); }
void L(int u, int v)
{
f[v = F(v)] = u = F(u);
s[u] += s[v];
}
void D(int u, int k)
{
for (int i = h[u], v; i; i = e[i].t)
if ((v = e[i].v) != k)
{
g[v] = u;
if (a[v] == a[u])
L(u, v);
else
C[F(u)][a[v]].push_back(v);
D(v, u);
}
}
void M(int x, int y)
{
if (C[x].size() < C[y].size())
swap(C[x], C[y]);
for (auto i : C[y])
C[x][i.first].splice(C[x][i.first].end(), i.second);
C[y].clear();
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 2, x; i <= n; ++i)
scanf("%d", &x), A(x, i);
for (int i = 1; i <= n; ++i)
scanf("%d", a + i), s[f[i] = i] = 1;
D(1, 0);
for (int i = 0, o, x, y, z; i < m; ++i)
{
scanf("%d%d", &o, &x);
x = F(x);
if (o & 1)
{
scanf("%d", &y);
if (a[x] == y)
continue;
auto l = C[x][y];
for (auto i : l)
if (y == a[i] && x != F(i))
L(x, i), M(x, i);
if (y == a[z = F(g[x])])
L(z, x), M(z, x);
else if (z)
C[z][y].push_back(x);
C[F(x)][a[x] = y].clear();
}
else
printf("%d\n", s[x]);
}
return 0;
}