首页 > 其他 > 详细

【Luogu P3950】部落冲突

时间:2020-01-03 23:31:03      阅读:109      评论:0      收藏:0      [点我收藏+]

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\) 操作都给出一行 Yes 或 No 代表询问结果。

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

【Luogu P3950】部落冲突

原文:https://www.cnblogs.com/luoshuitianyi/p/12147126.html

踩
(0)
赞
(0)
   
举报
评论 一句话评论(0)
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!