HDU 1540 Tunnel Warfare(线段树区间合并)
题目大意
三种操作,破坏一个点,修复最后被破坏的点,询问包含这个点的连续区间的最大长度。
解题思路
用线段树维护父节点的前缀最长区间和后缀最长区间。
父节点的前缀最长区间最小等于左二子的最长前缀区间,如果左儿子的最长前缀区间是整个区间,那么父节点的最长前缀区间则还可以加上右二子的最长前缀区间。父节点的后缀最长区间同理。
对于询问,询问的位置可能有两种情况,一时pos<=区间中点mid,一是pos>mid。
对于前者,如果从mid到pos是连续的,则包含mid的区间长度就是答案,也就是tree[rt<<1].post+tree[rt<<1|1].pre,否则继续缩小区间。
对于后者,如果从mid+1到pos时连续的,那么答案也和上面一样。
代码
const int maxn = 1e5+10;
struct Tree {
int l, r, len, pre, post;
} tree[maxn<<2];
int n, m;
inline void push_down(int rt) {
tree[rt].pre = tree[rt<<1].pre;
tree[rt].post = tree[rt<<1|1].post;
if (tree[rt<<1].pre==tree[rt<<1].len) tree[rt].pre = tree[rt<<1].pre+tree[rt<<1|1].pre;
if (tree[rt<<1|1].post==tree[rt<<1|1].len) tree[rt].post = tree[rt<<1|1].post+tree[rt<<1].post;
}
void build(int rt, int l, int r) {
tree[rt].l = l, tree[rt].r = r, tree[rt].len = r-l+1;
if (l==r) {
tree[rt].pre = tree[rt].post = 1;
return;
}
int mid = (l+r)>>1;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
push_down(rt);
}
void update(int rt, int pos, int val) {
if (tree[rt].l==tree[rt].r) {
tree[rt].post = tree[rt].pre = val;
return;
}
int mid = (tree[rt].l+tree[rt].r)>>1;
if (mid>=pos) update(rt<<1, pos, val);
else update(rt<<1|1, pos, val);
push_down(rt);
}
int quary(int rt, int pos) {
if (tree[rt].l==tree[rt].r) return tree[rt].pre;
//如果区间长度大于1,那么只有答案是0的时候会l=r,否则,可能是0,也可能是1
//总之区间能缩小到l=r,那么这点被破坏就是0,否则就是1
int mid = (tree[rt].l+tree[rt].r)>>1;
if (mid>=pos) {
if (tree[rt<<1].post+pos>mid) return tree[rt<<1].post+tree[rt<<1|1].pre;
//区间中点所在的区间左边能否包含pos
else return quary(rt<<1, pos);
}
else {
if (tree[rt<<1|1].pre+mid>=pos) return tree[rt<<1].post+tree[rt<<1|1].pre;
//区间中点+1所在的区间右边能否包含pos
else return quary(rt<<1|1, pos);
}
}
int main() {
while(cin >> n >> m) {
stack<int> sk;
build(1, 1, n);
for (int i = 1; i<=m; ++i) {
char ch[5]; scanf("%s", ch);
if (ch[0]=='D') {
int num; scanf("%d", &num);
update(1, num, 0); sk.push(num);
}
else if (ch[0]=='Q') {
int num; scanf("%d", &num);
printf("%d\n", quary(1, num));
}
else if (!sk.empty()) update(1, sk.top(), 1), sk.pop();
}
}
return 0;
}