Loading

Tunnel Warfare - HDU 1540

Link

题目分析

本题是一个线段树单点修改和区间查询的问题。

在题目中,每个点都是两两相连,整个区间内的点都是相互连通的。

根据题意,可以得到题目的操作:

  1. 如果点\(x\)能被访问,那就删除点\(x\),即解除它与它相邻两点的联通关系
  2. 询问点\(x\)与多少个点相连
  3. 如果点\(x\)被删除,那就恢复\(x\),使其与相邻的两点相连

我们可以将整个区间看作整个联通块:

  • 如果点\(x\)被删除,则产生了两个新联通块
  • 查询\(x\)与多少个点相连,等价查询\(x\)所在点联通块有多少个点
  • 恢复同理

我个人认为,本题的难点在于求\(x\)所在的联通块有多少个点。我们可以用一个set来存储被删除的点的下标,比如:对于序列\([1,\dots ,n]\),删除点\(3\)\(10\),那么序列就变成了\([1,2, 3, \dots , 10, 11,\dots, n]\),当我们要查询与\(5\)相连的点的数量,只需要获取线段树中区间\([3,5]\)中点的数量。

注意

本题细节比较多,需要注意以下几点

  1. set中查询被\(x\)的左右区间时:

    1. 寻找右区间时,set中第一个大于\(x\)的下标就是查询的右区间;如果不存在,那么查询的右区间则为\(n\)
    2. 寻找左区间时,先找第一个大于等于\(x\)的下标:
      • 如果找到的下标等于\(x\),说明\(x\)本身是被删除的点,返回0
      • 上述情况不满足时,如果找到的下标是set中最小的下标,那么查询的左边界是\(1\);如果不是,左区间为\(j-1\)
  2. 注意回复操作次数超过之前删除的操作数

  3. set本身会除去重复元素,由于存在一个点被多次删除的情况,所以删除set中的某个下标前,要判断是否存在

Code

#include <iostream>
#include <algorithm>
#include <set>
using namespace std;

constexpr int N = 50050;

struct Node {
  int l, r;
  int sum;
} tr[N * 4];

int n, m, stk[N], top, x;
set<int> se;

inline void push_up(int u) {
  tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void build(int u, int l, int r) {
  if (l == r) tr[u] = {l, r, 1};
  else {
    tr[u] = {l, r};
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    push_up(u);
  }
}
void modify(int u, int x, int c) {
  if (tr[u].l == x && tr[u].r == x) tr[u].sum = c;
  else {
    int mid = tr[u].l + tr[u].r >> 1;
    if (x <= mid) modify(u << 1, x, c);
    if (x > mid) modify(u << 1 | 1, x, c);
    push_up(u);
  }
}

int query(int u, int l, int r) {
  if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
  else {
    int mid = tr[u].l + tr[u].r >> 1, cnt = 0;
    if (l <= mid) cnt = query(u << 1, l, r);
    if (r > mid) cnt += query(u << 1 | 1, l, r);
    return cnt;
  }
}

int main() {
  char op[2];
  while (~scanf("%d%d", &n, &m)) {
    se.clear();
    top = 0;
    build(1, 1, n);
    while (m --) {
      scanf("%s", op);
      if (*op == 'R') {
        if (top) {
          int t = stk[top --];
          modify(1, t, 1);
          // 判断要删除的元素是否存在
          auto it = se.lower_bound(t);
          if (it != se.end() && *it == t) se.erase(it);
        }
      } else {
        scanf("%d", &x);
        if (*op == 'D') {
          // 删除操作,同时记录每次的操作
          modify(1, x, 0);
          stk[++top] = x;
          se.insert(x);
        }
        else if (*op == 'Q') {
          int l, r;
    			// 二分查找左右边界
          auto i = se.lower_bound(x), j = se.upper_bound(x);
          if (i != se.end() && *i == x) { puts("0"); continue; }
          else {
            if (i == se.begin()) l = 1;
            else l = *(--i);
          }

          if (j == se.end()) r = n;
          else r = *j;
          printf("%d\n", query(1, l, r));
        }
      }
    }
  }

  return 0;
}
posted @ 2022-03-29 22:27  Frank_Ou  阅读(27)  评论(0编辑  收藏  举报