题意:有n个村庄在一条直线上,编号从1到n,每个村庄和相邻的两个村庄有一条边,三种操作,D a表示把村庄a摧毁,Q a表示询问和村庄a连通的村庄数量,R把最近摧毁的村庄复原。
题解:线段树维护区间左右端点延伸后的连通村庄数量,Q操作可以通过查询起点和终点来计算数量。因为是复原最近摧毁的村庄所以可以用栈来保存。然后查询操作注意先递归再比较是否可以把起点终点继续扩展。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 50005;
int n, q, lenl[N << 2], lenr[N << 2], st, en;
stack<int> s;
void pushup(int k, int left, int right) {
int mid = (left + right) / 2;
lenl[k] = lenl[k * 2];
lenr[k] = lenr[k * 2 + 1];
if (lenl[k * 2] == mid - left + 1)
lenl[k] += lenl[k * 2 + 1];
if (lenr[k * 2 + 1] == right - mid)
lenr[k] += lenr[k * 2];
}
void build(int k, int left, int right) {
lenl[k] = lenr[k] = right - left + 1;
if (left != right) {
int mid = (left + right) / 2;
build(k * 2, left, mid);
build(k * 2 + 1, mid + 1, right);
}
}
void modify(int k, int left, int right, int pos, int v) {
if (left == right) {
if (v == 1)
lenl[k] = lenr[k] = 1;
else
lenl[k] = lenr[k] = 0;
return;
}
int mid = (left + right) / 2;
if (pos <= mid)
modify(k * 2, left, mid, pos, v);
else
modify(k * 2 + 1, mid + 1, right, pos, v);
pushup(k, left, right);
}
void query(int k, int left, int right, int pos) {
if (left == right) {
if (lenl[k] == 1)
st = en = left;
else st = en = 0;
return;
}
int mid = (left + right) / 2;
if (pos <= mid)
query(k * 2, left, mid, pos);
else
query(k * 2 + 1, mid + 1, right, pos);
if (mid == en)
en += lenl[k * 2 + 1];
if (mid + 1 == st) {
st -= lenr[k * 2];
}
}
int main() {
while (scanf("%d%d", &n, &q) == 2) {
build(1, 1, n);
while (!s.empty())
s.pop();
char op[5];
int a;
while (q--) {
scanf("%s", op);
if (op[0] == 'Q') {
scanf("%d", &a);
query(1, 1, n, a);
if (!st) printf("0\n");
else printf("%d\n", en - st + 1);
}
else if (op[0] == 'D') {
scanf("%d", &a);
s.push(a);
modify(1, 1, n, a, 0);
}
else {
modify(1, 1, n, s.top(), 1);
s.pop();
}
}
}
return 0;
}