观察

题目大意

给出一颗以 \(1\) 为根且有 \(n\) 个节点的树,一开始每个节点都是一颗棋子,一面白一面黑,白色的面朝上。

接下来就 \(q\) 次操作,操作分两种:

  • \(0\) 操作,将一颗棋子翻转。
  • \(1\) 操作,询问一颗棋子与所有面朝上为黑色的棋子的 LCA 最深LCA 的编号(若当前没有黑棋子输出 \(0\))。

对于 \(100 \%\) 的数据,\(1 ≤ n ≤ 800000\), \(1 ≤ q ≤ 800000\)。

解题思路

思路应该算是比较好想的了。

用树状数组或线段树加树剖维护子树内黑色朝上的棋子的个数。

对于操作 \(0\),非常简单,不讲了。

对于操作 \(1\),每次类似于树剖跳链,不同的是一直跳父亲,判断当前跳到的节点的子树内是否有黑棋,有就直接输出。(这样做的理由是,一个节点与另一个节点的 LCA 只可能存在于两个节点中任意一个节点到根节点的简单路径上)

注意,本题数据过大,可能会爆栈,可以吸氧。

AC CODE

#include <bits/stdc++.h>
#define _ (int)800000 + 5
using namespace std;

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;
}

int n, m;

int fa[_];

int a[_];

bool tag[_];

int tot, head[_], to[_ << 1], nxt[_ << 1];

int cnt_node, siz[_], dfn[_], hson[_];

void add(int u, int v)
{
    to[++tot] = v;
    nxt[tot] = head[u];
    head[u] = tot;
}

void dfs1(int u)
{
    siz[u] = 1;
    for (int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (siz[v])
            continue;
        dfs1(v);
        siz[u] += siz[v];
        if (siz[hson[u]] < siz[v])
            hson[u] = v;
    }
}

void dfs2(int u)
{
    dfn[u] = ++cnt_node;
    if (!hson[u])
        return;
    dfs2(hson[u]);
    for (int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (!dfn[v])
            dfs2(v);
    }
}

void update(int x)
{
    int y = 1;
    if (tag[x])
        y = -1;
    tag[x] ^= 1;
    for (int i = x; i <= n; i += i & -i)
        a[i] += y;
}

int query(int x)
{
    int res = 0;
    for (int i = x; i > 0; i -= i & -i)
        res += a[i];
    return res;
}

bool Query(int x)
{
    return query(dfn[x] + siz[x] - 1) - query(dfn[x] - 1);
}

int solve(int x)
{
    while (x)
    {
        if (Query(x))
            return x;
        x = fa[x];
    }
    return 0;
}

signed main()
{
    n = read();
    m = read();
    for (int i = 2; i <= n; ++i)
    {
        fa[i] = read();
        add(fa[i], i);
        add(i, fa[i]);
    }
    dfs1(1);
    dfs2(1);
    for (int i = 1; i <= m; ++i)
    {
        int x;
        x = read();
        if (x > 0)
        {
            update(dfn[x]);
        }
        else
        {
            printf("%d\n", solve(-x));
        }
    }
    return 0;
}
上一篇:洛谷P4216


下一篇:虚树