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;
}
posted @ 2023-04-19 18:47  5k_sync_closer  阅读(2)  评论(0编辑  收藏  举报  来源