• 【Luogu P3950】部落冲突


    Problem

    Description

    给出一棵树。

    你需要处理下面三件事,所有的事件都是按照时间顺序给出的。

    1. (Q, u, v) 代表询问 (u, v) 之间能否相互到达

    2. (C, u, v) 代表 (u, v) 之间的边断开了

    3. (U, x) 代表第 (U)(C) 操作被还原

    Input Format

    第一行两个数 (n)(m)(n) 代表了一共有 (n) 个部落,(m) 代表了以上三种事件发生的总数

    接下来的 (n - 1) 行,每行两个数 (p, q) ,代表了第 (p) 个点与第 (q) 个点之间有一条道路相连

    接下来的 (m) 行,每行表示一个操作,详见题目描述。

    Output Format

    对于每个 (Q) 操作都给出一行 YesNo 代表询问结果。

    Range

    (n, mle 3*10^5)

    Algorithm

    树状数组

    Mentality

    其实本来是想练手一下 (LCT) 的,但是这题用 (LCT) 实在显得牛刀杀鸡 ……

    大致看了一下题解,都用了树剖和 (LCT) 这样的,但其实有一种很简单的方法,只需要树状数组 + 随机数。

    根据题目,对于一条路径 (u, v) ,这条路径联通当且仅当 (u, v) 上的边都没有被断开。

    那么如何才能满足这个条件呢?换成对断边的要求,就是对于当前所有断开了的边,(u, v) 要么都在它的子树内,要么都不在。

    则我们可以考虑用 (dfn) 序 + 树状数组维护子树信息,只要在每次断边的时候,给子树内每个点都赋上一个特有信息,然后询问的时候对 (u, v) 查询上面的信息是否相等即可。

    如何赋上这个特殊信息呢?很简单,对于每次断边都 (rand) 一个特定权值,用树状数组给子树内的所有节点都加上这个权值即可。

    查询的时候只需要查一下两点权值是否相同就行。

    还原的时候再删掉就好了。

    Code

    #include <cstdio>
    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    using namespace std;
    #define LL long long
    #define go(G, x, i, v) 
      for (int i = G.hd[x], v = G.to[i]; i; v = G.to[i = G.nx[i]])
    #define inline __inline__ __attribute__((always_inline))
    inline LL read() {
      LL x = 0, w = 1;
      char ch = getchar();
      while (!isdigit(ch)) {
        if (ch == '-') w = -1;
        ch = getchar();
      }
      while (isdigit(ch)) {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
      }
      return x * w;
    }
    
    const int Max_n = 3e5 + 5, mod = 998244353;
    int n, m;
    int cntd, dep[Max_n], dfn[Max_n], siz[Max_n];
    int c[Max_n];
    struct graph {
      int hd[Max_n];
      int cntr, nx[Max_n << 1], to[Max_n << 1];
      void addr(int u, int v) {
        cntr++;
        nx[cntr] = hd[u], to[cntr] = v;
        hd[u] = cntr;
      }
    } G;
    struct que {
      int u, v, x;
    } k[Max_n];
    
    namespace Input {
    void main() {
      n = read(), m = read();
      int u, v;
      for (int i = 1; i < n; i++) {
        u = read(), v = read();
        G.addr(u, v), G.addr(v, u);
      }
    }
    }  // namespace Input
    
    namespace Init {
    void build(int x, int fa) {
      dep[x] = dep[fa] + 1, siz[x] = 1, dfn[x] = ++cntd;
      go(G, x, i, v) if (v != fa) build(v, x), siz[x] += siz[v];
    }
    void main() { build(1, 0); }
    }  // namespace Init
    
    namespace Solve {
    void add(int k, int x) {
      for (int i = k; i <= n; i += i & -i) (c[i] += x) %= mod;
    }
    int query(int k) {
      int ans = 0;
      for (int i = k; i; i -= i & -i) (ans += c[i]) %= mod;
      return (ans + mod) % mod;
    }
    void main() {
      srand((unsigned)time(NULL));
      int cnt = 0, u, v;
      char op;
      for (int i = 1; i <= m; i++) {
        scanf(" %c", &op);
        u = read();
        if (op == 'U')
          add(dfn[k[u].v], -k[u].x), add(dfn[k[u].v] + siz[k[u].v], k[u].x);
        else {
          v = read();
          if (op == 'Q')
            printf("%s
    ", query(dfn[u]) == query(dfn[v]) ? "Yes" : "No");
          else {
            k[++cnt].x = rand() % mod;
            if (dep[u] > dep[v]) swap(u, v);
            add(dfn[v], k[cnt].x), add(dfn[v] + siz[v], -k[cnt].x);
            k[cnt].u = u, k[cnt].v = v;
          }
        }
      }
    }
    }  // namespace Solve
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("3950.in", "r", stdin);
      freopen("3950.out", "w", stdout);
    #endif
      Input::main();
      Init::main();
      Solve::main();
    }
    
  • 相关阅读:
    静态资源分析 ------ CocosCreator
    性能分析 ------ CPU运行卡点
    神犇的blog
    0x01-1 原码 反码 补码 概念 原理 详解
    埃拉托色尼素数筛法(转)
    Miller-Rabin概率素数测试算法(转)
    欧拉函数(转)
    中国剩余定理(孙子定理)详解 (转)
    负数取模(转)
    HDU1430 BFS + 打表 + 康托展开(转)
  • 原文地址:https://www.cnblogs.com/luoshuitianyi/p/12147126.html
Copyright © 2020-2023  润新知