[POI 2021/2022 R2] kon 题解

前言

题目链接:洛谷

题意简述

维护一张无向图图,初始两个点,通过一条边相连。\(q\) 次操作:

  • W x:新建点,和 \(x\) 连边。
  • Z x:新建点,和 \(x\) 所有直接相连的点连边。
  • ? x:查询有几个点和 \(x\) 直接相连。

题目分析

妙妙题。

发现 Z 操作很像把 \(x\) 复制一遍,相当于一个新的版本。提示我们对操作建树,\(u\) 继承了 \(\operatorname{fa}(u)\) 所有对应的连边状态。

那么 W 操作就是先把 \(x\) 复制一遍,在新版 \(x\) 上与新建点「连边」(这里连边是原图上相连的意思,并非树边,称之为「W 边」),这样不会让之前的点错误继承这次操作。

原图上,两个点 \(u,v\) 相连,当且仅当操作树上 \(u\) 的某一个祖先 \(u'\)\(v\) 的某一个祖先 \(v'\),通过 W 边相连。

那么问题变为了求 \(x\) 的每一个祖先,所有连出 W 边指向的点,在操作树上的子树中有几个点,这里需要对点做区分,不能算上 W 操作复制的版本。

稍微分类讨论一下。对于树根,相当于单点加子树和;对于非树根,修改的时候将树根 W 边父亲单点修改,链查询。

均可以树状数组做到 \(\mathcal{O}(n\log n)\)

可以 LCT 做到在线,相信大家都会。

代码

离线树状数组
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

const int N = 1e6 + 10;
const int M = N * 2;

int tot, n, q, qid;
enum QType { ADD, SUB, QUERY };
struct {
    QType op;
    int u;
} qry[N * 3];

int rl[N];  // u 实际在操作树上的点(最新版本)

int root[M], Wfa[M], fa[M];
vector<int> e[M];

inline int newNode() {
    return rl[++tot] = ++n;
}

inline void linkW(int u) {
    int x = rl[u];
    e[x].emplace_back(rl[u] = ++n);  // 新建版本
    qry[++qid] = { SUB, x };
    qry[++qid] = { ADD, rl[u] };
    fa[rl[u]] = x;
    int v = newNode();
    qry[++qid] = { ADD, v };
    Wfa[v] = rl[u];
}

inline void linkZ(int u) {
    int v = newNode();
    qry[++qid] = { ADD, v };
    e[rl[u]].emplace_back(v);
    fa[v] = rl[u];
}

int dfn[M], timer, R[M];

void dfs(int u) {
    dfn[u] = ++timer;
    for (int v : e[u]) root[v] = root[u], dfs(v);
    R[u] = timer;
}

struct {
    int t[M];
    inline void upd(int p, int v) {
        for (; p <= n; p += p & -p) t[p] += v;
    }
    inline void upd(int l, int r, int v) {
        upd(l, v), upd(r + 1, -v);
    }
    inline int qry(int p) {
        int r = 0;
        for (; p; p &= p - 1) r += t[p];
        return r;
    }
    inline int qry(int l, int r) {
        return qry(r) - qry(l - 1);
    }
} t[2];

signed main() {
    rl[1] = 1, tot = n = 1;
    qry[++qid] = { ADD, 1 };
    linkW(1);
    scanf("%d", &q);
    for (int u; q--; ) {
        char s[2];
        scanf("%s%d", s, &u);
        if (*s == 'W') linkW(u);
        else if (*s == 'Z') linkZ(u);
        else qry[++qid] = { QUERY, rl[u] };
    }
    for (int i = 1; i <= n; ++i)
        if (!dfn[i]) {
            root[i] = i;
            dfs(i);
        }
    for (int i = 1; i <= qid; ++i) {
        int u = qry[i].u;
        if (qry[i].op != QUERY) {
            int v = qry[i].op == SUB ? -1 : 1;
            t[0].upd(dfn[u], v);
            u = Wfa[root[u]];
            if (u) t[1].upd(dfn[u], R[u], v);
        } else {
            int r = t[1].qry(dfn[u]);
            u = Wfa[root[u]];
            if (u) r += t[0].qry(dfn[u], R[u]);
            printf("%d\n", r);
        }
    }
    return 0;
}
在线 LCT
#include <cstdio>
#include <iostream>
using namespace std;

const int N = 2e6 + 10;

#define ls s[0]
#define rs s[1]

struct {
    int f, s[2];
    int v, tg, w, sm;
    bool r;
} t[N];

inline bool isR(int x) {
    return x != t[t[x].f].ls && x != t[t[x].f].rs;
}
inline bool get(int x) {
    return x == t[t[x].f].rs;
}
inline void U(int x) {
    t[x].sm = t[t[x].ls].sm + t[x].w + t[t[x].rs].sm;
}
inline void Ro(int x) {
    int y = t[x].f, z = t[y].f;
    bool o = get(x);
    if (!isR(y)) t[z].s[get(y)] = x;
    t[x].f = z;
    t[t[y].s[o] = t[x].s[!o]].f = y;
    t[t[x].s[!o] = y].f = x;
    U(y), U(x);
}
inline void P(int u, int v) {
    if (u) t[u].tg += v, t[u].v += v;
}
inline void R(int u) {
    if (u) t[u].r ^= 1, swap(t[u].ls, t[u].rs);
}
inline void PD(int u) {
    if (t[u].r) R(t[u].ls), R(t[u].rs), t[u].r = false;
    if (t[u].tg) P(t[u].ls, t[u].tg), P(t[u].rs, t[u].tg), t[u].tg = 0;
}
inline void D(int u) {
    if (!isR(u)) D(t[u].f);
    PD(u);
}
inline void S(int u) {
    D(u);
    for (int v = t[u].f; !isR(u); Ro(u), v = t[u].f)
        if (!isR(v)) Ro(get(u) == get(v) ? v : u);
}
inline int A(int u) {
    int o = 0;
    while (u)
        S(u), t[u].rs = o, U(u), o = u, u = t[u].f;
    return o;
}
inline void MR(int u) {
    R(A(u));
}
inline void link(int u, int v) {
    t[v].f = u;
}

#undef ls
#undef rs

int tot, n, q;
int rl[N], Wfa[N], RT[N];
void AD(int u, int w) {
    MR(RT[u]);
    P(A(u), w);
    u = Wfa[RT[u]];
    if (u) S(u), t[u].w += w, t[u].sm += w;
}
inline int gen() {
    return rl[++tot] = ++n;
}
inline void linkW(int u) {
    int x = rl[u];
    rl[u] = ++n;
    RT[rl[u]] = RT[x];
    AD(x, -1);
    link(x, rl[u]);
    AD(rl[u], 1);
    int v = gen();
    RT[v] = v;
    Wfa[v] = rl[u];
    AD(v, 1);
}
inline void linkZ(int u) {
    int v = gen();
    RT[v] = RT[rl[u]];
    link(rl[u], v);
    AD(v, 1);
}

signed main() {
    rl[1] = 1, RT[1] = 1, tot = n = 1, AD(1, 1);
    linkW(1);
    scanf("%d", &q);
    for (int u; q--; ) {
        char s[2];
        scanf("%s%d", s, &u);
        if (*s == 'W') linkW(u);
        else if (*s == 'Z') linkZ(u);
        else {
            u = rl[u];
            MR(RT[u]);
            int r = t[A(u)].sm;
            u = Wfa[RT[u]];
            if (u) S(u), r += t[u].v;
            printf("%d\n", r);
        }
    }
    return 0;
}
posted @ 2025-06-20 12:12  XuYueming  阅读(60)  评论(0)    收藏  举报