观察

Posted 蒟蒻orz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了观察相关的知识,希望对你有一定的参考价值。

题目大意

给出一颗以 \\(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;
}

本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/15228109.html

以上是关于观察的主要内容,如果未能解决你的问题,请参考以下文章

如何为片段设置观察者

如何让自定义视图观察包含片段的生命周期事件而不是活动?

从没有中间转换变量的片段中观察 ViewModel LiveData

golang 转到片段以观察运行时行为和内存分配

KnockoutJS调用可观察的属性

设计模式 行为型模式 -- 观察者模式(发布-订阅(Publish/Subscribe)模式)