题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1540
题目大概的意思就是求当前这个村庄,左右连续的村庄共有几个,包括自己。
思路:
比较容易想到的就是把用线段树划分的每一个区间的左右连续区间长度记录下来,然后尝试着吧X这个村庄的左右连续并且没被摧毁的村庄个数连起来,就能得出答案了。
具体看代码的注释
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
using namespace std;
typedef long long ll;
const int Maxn = 5e4+10;
const int INF = 0x3f3f3f3f;
int a[Maxn], Left[4*Maxn], Right[4*Maxn], zero[4*Maxn], X; // zero用来记录当前区间有几个被摧毁的村庄
bool L, R, op; // L, R分别表示左右界有没有找到,op == 1 表示摧毁
void init(int cur, int l, int r) {
if (l == r) {
if(a[l]) Left[cur] = Right[cur] = 1;
else zero[cur] = 1;
return;
}
int mid = (r+l)/2;
init(cur*2, l, mid); init(cur*2+1, mid+1, r);
Left[cur] = Left[cur*2]; Right[cur] = Right[cur*2+1]; // 如果左子树全都是1,那么当前区间的左连续长度还需要
if (!zero[cur*2]) Left[cur] += Left[cur*2+1]; // 加上右子树的左连续区间,以此类推
if (!zero[cur*2+1]) Right[cur] += Right[cur*2]; // 通过zero能判断当前区间是否全都是1
}
void updata(int cur, int l, int r) {
if (l == r) {
if (X == l) {
if (op && a[l]) { // op == 1, 表示摧毁村庄
a[l] = 0; zero[cur] = 1;
Left[cur] = Right[cur] = 0;
} else if (!op && !a[l]) {
a[l] = 1; zero[cur] = 0;
Left[cur] = Right[cur] = 1;
}
}
return;
}
int mid = (r+l)/2;
if (X <= mid) updata(cur*2, l, mid); // 如果修改的是左子树,右子树可以不用递归,但是右子树的左连续区间可能会
else updata(cur*2+1, mid+1, r); // 因为左子树的修改而发生变化,所以右子树的区间记录也要更新
Left[cur] = Left[cur*2]; Right[cur] = Right[cur*2+1];
if (!zero[cur*2]) Left[cur] += Left[cur*2+1];
if (!zero[cur*2+1]) Right[cur] += Right[cur*2];
zero[cur] = zero[cur*2]+ zero[cur*2+1];
}
int solve(int cur, int l, int r) {
if (l == r) {
return Left[cur];
}
int m1, m2, mid = (r+l)/2;
if (X <= mid) { // 如果当前是左子树,
m1 = solve(cur*2, l, mid); // 如果找到右界,或者左右界都找到了,那么就不需要右子树的左连续区间了,
m2 = Left[cur*2+1]; // 否则要加上右子树的左连续区间,并且判断右子树有没有被摧毁的村庄,
if (R) return m1; // 如果有,那么说明找右界了。下面的右子树也是同理。
else {
if (zero[cur*2+1]) R = true;
return m1+m2;
}
} else {
m1 = solve(cur*2+1, mid+1, r);
m2 = Right[cur*2];
if (L) return m1;
else {
if (zero[cur*2]) L = true;
return m1+m2;
}
}
}
int main (void)
{
int N, M;
while(scanf ("%d%d", &N, &M) != EOF) {
for (int i = 1; i <= N; ++i) a[i] = 1;
memset(Left, 0, sizeof(Left));
memset(Right, 0, sizeof(Right));
memset(zero, 0 ,sizeof(zero));
init(1, 1, N);
char ch; stack<int> st;
while (M--) {
scanf(" %c", &ch);
if (ch == 'R') {
op = false; X = st.top(); st.pop();
updata(1, 1, N);
} else if (ch == 'D') {
op = true; scanf("%d", &X);
st.push(X); updata(1, 1, N);
} else {
scanf("%d", &X);
L = R = false;
if(!a[X]) printf("0\n");
else printf("%d\n", solve(1, 1, N));
}
}
}
return 0;
}