N 個村莊排成線,初始全連通。三種操作:
D pos:摧毀村莊 pos(斷開連接)Q pos:查詢與 pos 連通的村莊數R:恢復最近被摧毀的村莊(棧式撤銷)區間 [L, R] 連通 ⇔ 區間內 1 的個數 = R-L+1。用樹狀數組維護前綴和,二分查找最遠連通邊界。
add(i, 1)D pos:add(pos, -1),將 pos 壓入棧R:彈棧得 pos,add(pos, 1)Q pos:
re 使 sum(re)-sum(pos-1) == re-pos+1rs 使 sum(pos)-sum(rs-1) == pos-rs+1re - rs + 1#include <bits/stdc++.h>
using namespace std;
const int MAXN = 55555;
int c[MAXN], stk[MAXN], top, n, m;
int lowbit(int x) { return x & -x; }
void add(int x, int d) { for (; x <= n; x += lowbit(x)) c[x] += d; }
int sum(int x) { int r = 0; for (; x; x -= lowbit(x)) r += c[x]; return r; }
int main() {
while (scanf("%d%d", &n, &m) == 2) {
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; ++i) add(i, 1);
top = 0;
char op[5];
while (m--) {
scanf("%s", op);
if (op[0] == 'D') {
int pos; scanf("%d", &pos);
if (sum(pos) - sum(pos-1) == 1) { // 僅未摧毀時操作
add(pos, -1);
stk[top++] = pos;
}
} else if (op[0] == 'R') {
if (top) add(stk[--top], 1);
} else { // Q
int pos; scanf("%d", &pos);
if (sum(pos) - sum(pos-1) == 0) { puts("0"); continue; }
int l = pos, r = n, re = pos;
while (l <= r) {
int mid = (l + r) >> 1;
if (sum(mid) - sum(pos-1) == mid - pos + 1) {
re = mid; l = mid + 1;
} else r = mid - 1;
}
l = 1; r = pos; int rs = pos;
while (l <= r) {
int mid = (l + r) >> 1;
if (sum(pos) - sum(mid-1) == pos - mid + 1) {
rs = mid; r = mid - 1;
} else l = mid + 1;
}
printf("%d\n", re - rs + 1);
}
}
}
return 0;
}
R 操作天然後進先出,棧完美匹配並查集擅長合並、查找,但不支持刪除邊(摧毀村莊)。本題「撤銷」操作實為時光倒流,棧+樹狀數組是經典解法。