题目链接
题意:D代表破坏村庄,R代表修复最后被破坏的那个村庄,Q代表询问包括x在内的最大连续区间是多少?
需要用线段树维护区间的3个变量:
区间最长的:[左连续前缀、右连续后缀、最大连续子区间] 的元素个数。
其中红线左侧的是第m个元素,右侧是m+1个元素。
更新完左右子树想要pushUp操作的时候,父亲的左前缀必然是左子的前缀,若是左子树满了,则左连续可能还要加上右子树的左前缀。同理,父亲的右后缀必然是右子的后缀,若是右子都连续,那么还要加上左子的后缀。
查询的时候,若是位置p在左子的“橙色“部分,即后缀中,那么结果应当是左子中的连续部分+(m+1)(即m的下一个元素)在右子的连续部分,同理p在右子的紫色部分时,要考虑加上左子的后缀部分。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(ll i=(a);i<(b);++i)
const int maxN = 5e4 + 5;
int N, M, T;
int stk[maxN];
#define lch rt << 1
#define rch rt << 1 | 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
int lq[maxN << 2], rq[maxN << 2], aq[maxN << 2];
void pushUp(int l, int r, int rt) {
int m = (l + r) / 2;
lq[rt] = lq[lch];
rq[rt] = rq[rch];
int zy = max(lq[lch], rq[rch]);
aq[rt] = max(zy, rq[lch] + lq[rch]);
if (lq[lch] == m - l + 1)
lq[rt] += lq[rch];
if (rq[rch] == r - m)
rq[rt] += rq[lch];
}
void init(int l, int r, int rt) {
lq[rt] = rq[rt] = aq[rt] = r - l + 1;
if (l == r)
return;
int m = (l + r) / 2;
init(lson);
init(rson);
pushUp(l, r, rt);
}
void update(int p, int x, int l, int r, int rt) {
if (l == r) {
lq[rt] = rq[rt] = aq[rt] = x;
return;
}
int m = (l + r) / 2;
if (p <= m) update(p, x, lson);
else update(p, x, rson);
pushUp(l, r, rt);
}
int query(int p, int l, int r, int rt) {
if (aq[rt] == r - l + 1 || aq[rt] == 0 || l == r)
return aq[rt];
int m = (l + r) >> 1;
if (p <= m) {
if (p >= m - rq[lch] + 1)
return query(p, lson) + lq[rch];
else
return query(p, lson);
} else {
if (p <= m + lq[rch])
return query(p, rson) + rq[lch];
else
return query(p, rson);
}
}
int main() {
// freopen("data.in", "r", stdin);
while (~scanf("%d%d", &N, &M)) {
char op[10];
int a, idx = 0;
init(1, N, 1);
rep(i, 0, M) {
scanf("%s", op);
if (op[0] == 'R') {
update(stk[--idx], 1, 1, N, 1);
continue;
}
scanf("%d", &a);
if (op[0] == 'Q') {
printf("%d\n", query(a, 1, N, 1));
} else {
update(a, 0, 1, N, 1);
stk[idx++] = a;
}
}
}
return 0;
}
网友评论