【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, m\le 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\n", 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();
}
posted @ 2020-01-03 21:51  洛水·锦依卫  阅读(181)  评论(0编辑  收藏  举报