SP32952 ADAFTBLL - Ada and Football 题解
树上带修莫队板子。
考虑怎么把序列莫队搬到树上。
设 $u$ 的子树在树的括号序列上对应 $[s_u,t_u]$。
则对于询问 $u,v$,钦定 $s_u<s_v$,分情况讨论:
- $u=\operatorname{lca}(u,v)$:$[s_u,s_v]$ 中出现一次的点对本次询问有贡献。
- $u\ne\operatorname{lca}(u,v)$:$[t_u,s_v]$ 中出现一次的点与 $\operatorname{lca}(u,v)$ 对本次询问有贡献。
扫描括号序列,用一个标记数组 $v_u$ 表示点 $u$ 是否有贡献,扫描到点 $u$ 时把 $v_u$ 取反即可。
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
struct E
{
int v, t;
} e[200050];
struct Q
{
int l, r, t, k, i;
} q1[100050], q2[100050];
bool v[100050];
int n, m, c, c1, c2, p, g, a[100050], s[100050], t[100050], d[100050], h[100050], C[100050], b[200050], T[200050], z[100050], f[100050][20];
void A(int u, int v)
{
e[++c] = {v, h[u]};
h[u] = c;
}
void D(int u)
{
b[s[u] = ++g] = u;
for (int i = h[u], v; i; i = e[i].t)
if (!d[v = e[i].v])
{
d[v] = d[f[v][0] = u] + 1;
for (int j = 1; f[v][j - 1]; ++j)
f[v][j] = f[f[v][j - 1]][j - 1];
D(v);
}
b[t[u] = ++g] = u;
}
int L(int x, int y)
{
if (d[x] < d[y])
swap(x, y);
while (d[x] > d[y])
x = f[x][__lg(d[x] - d[y])];
if (x == y)
return x;
for (int k = __lg(d[x]); k >= 0; --k)
if (f[x][k] != f[y][k])
x = f[x][k], y = f[y][k];
return f[x][0];
}
void I(int x) { p += C[x]++; }
void O(int x) { p -= --C[x]; }
void M(int x, int i)
{
if (v[q2[x].l])
O(a[q2[x].l]), I(q2[x].r);
swap(a[q2[x].l], q2[x].r);
}
bool B(Q a, Q b) { return T[a.l] == T[b.l] ? T[a.r] == T[b.r] ? T[a.r] & 1 ? a.t < b.t : a.t > b.t : T[a.l] & 1 ? a.r < b.r
: a.r > b.r
: a.l < b.l; }
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", a + i);
for (int i = 1, u, v; i < n; ++i)
scanf("%d%d", &u, &v), A(++u, ++v), A(v, u);
D(d[1] = 1);
for (int i = 1, k = pow(n <<= 1, 2.0 / 3); i <= n; ++i)
T[i] = (i - 1) / k + 1;
for (int i = 0, l, o, u, v; i < m; ++i)
{
scanf("%d%d%d", &o, &u, &v);
if (o & 1)
q2[++c2] = {++u, v};
else
{
if (s[++u] > s[++v])
swap(u, v);
q1[++c1].r = s[v];
q1[c1].i = c1;
q1[c1].t = c2;
if ((l = L(u, v)) == u)
q1[c1].l = s[u];
else
q1[c1].l = t[u], q1[c1].k = l;
}
}
sort(q1 + 1, q1 + c1 + 1, B);
for (int i = 1, l = 1, r = 0, t = 0; i <= c1; ++i)
{
while (l > q1[i].l)
if (v[b[--l]] ^= 1)
I(a[b[l]]);
else
O(a[b[l]]);
while (r < q1[i].r)
if (v[b[++r]] ^= 1)
I(a[b[r]]);
else
O(a[b[r]]);
while (l < q1[i].l)
if (v[b[l]] ^= 1)
I(a[b[l++]]);
else
O(a[b[l++]]);
while (r > q1[i].r)
if (v[b[r]] ^= 1)
I(a[b[r--]]);
else
O(a[b[r--]]);
while (t < q1[i].t)
M(++t, i);
while (t > q1[i].t)
M(t--, i);
if (q1[i].k)
I(a[q1[i].k]);
z[q1[i].i] = p;
if (q1[i].k)
O(a[q1[i].k]);
}
for (int i = 1; i <= c1; ++i)
printf("%d\n", z[i]);
return 0;
}