HDU - Tunnel Warfare(线段树|set)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1540
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)

Problem Description

During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly connected with two neighboring ones.

Frequently the invaders launched attack on some of the villages and destroyed the parts of tunnels in them. The Eighth Route Army commanders requested the latest connection state of the tunnels and villages. If some villages are severely isolated, restoration of connection must be done immediately!

Input

The first line of the input contains two positive integers n and m (n, m ≤ 50,000) indicating the number of villages and events. Each of the next m lines describes an event.

There are three different events described in different format shown below:

  • D x: The x-th village was destroyed.
  • Q x: The Army commands requested the number of villages that x-th village was directly or indirectly connected with including itself.
  • R: The village destroyed last was rebuilt.

Output

Output the answer to each of the Army commanders’ request in order on a separate line.

Sample Input

7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4

Sample Output

1
0
2
4

Problem solving report:

Description: 一段长为n的区间,m次操作,Q为查询x所在区间的最大长度,D为将X破坏(不连通),R为将最后一个破坏的点修复(连通)。
Problem solving: 线段树,维护3个值:

  1. 区间前缀长度
  2. 区间后缀长度
  3. 区间里最长连续区间

用栈存储一下破坏的点,在维护父节点前缀(后缀)时优先继承左孩子前缀(右孩子后缀),如果左孩子前缀(右孩子后缀)的长度等于区间长度应再加上右孩子前缀(左孩子后缀)。

其实,找含x的最大连续区间,事实上就是在x左侧找离x最近的删除点和在x右侧找离x最近的删除点
我们可以利用红黑树set的快速删除、添加、查询的优点来解决这道题。

Accepted Code:

//线段树
/* 
 * @Author: lzyws739307453 
 * @Language: C++ 
 */
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 5;
struct Tree {
    int ls, rs, sm;
}seg[MAXN << 2];
void Create(int l, int r, int rt) {
    if (!(r - l)) {
        seg[rt].ls = seg[rt].rs = seg[rt].sm = 1;
        return ;
    }
    int mid = l + (r - l >> 1);
    Create(l, mid, rt << 1);
    Create(mid + 1, r, rt << 1 | 1);
    seg[rt].ls = seg[rt].rs = seg[rt].sm = r - l + 1;
}
void Update(int k, int v, int l, int r, int rt) {
    if (!(r - l)) {
        seg[rt].ls = seg[rt].rs = seg[rt].sm = v;
        return ;
    }
    int mid = l + (r - l >> 1);
    if (k <= mid)
        Update(k, v, l, mid, rt << 1);
    else Update(k, v, mid + 1, r, rt << 1 | 1);
    seg[rt].ls = seg[rt << 1].ls;//父节点继承左孩子的前缀
    seg[rt].rs = seg[rt << 1 | 1].rs;
    if (seg[rt].ls > mid - l)//如果左孩子的前缀等于区间长度(左孩子满了)
        seg[rt].ls += seg[rt << 1 | 1].ls;//加上右孩子的前缀
    if (seg[rt].rs >= r - mid)
        seg[rt].rs += seg[rt << 1].rs;
    //父节点的sum为 左右孩子的最大值 与 左右孩子连起来的区间区最大值
    seg[rt].sm = max(max(seg[rt << 1].sm, seg[rt << 1 | 1].sm), seg[rt << 1].rs + seg[rt << 1 | 1].ls);
}
int Query(int k, int l, int r, int rt) {
    //叶子结点 或 完全连续 或 完全不连续 直接返回
    if (!(r - l) || seg[rt].sm >= r - l + 1 || !seg[rt].sm)
        return seg[rt].sm;
    int mid = l + (r - l >> 1);
    if (k < mid - seg[rt << 1].rs + 1)//在中间地带左侧
        return Query(k, l, mid, rt << 1);
    if (k > mid + seg[rt << 1 | 1].ls)//在中间地带右侧 
        return Query(k, mid + 1, r, rt << 1 | 1);
    return seg[rt << 1].rs + seg[rt << 1 | 1].ls;//在中间地带直接求和
}
int main() {
    char op;
    int n, m, k;
    while (~scanf("%d%d", &n, &m)) {
        stack <int> S;
        Create(1, n, 1);
        while (m--) {
            scanf(" %c", &op);
            if (op != 'Q') {
                if (op != 'R') {
                    scanf("%d", &k), S.push(k);
                    Update(k, 0, 1, n, 1);
                }
                else {
                    k = S.top(), S.pop();
                    Update(k, 1, 1, n, 1);
                }
            }
            else {
                scanf("%d", &k);
                printf("%d\n", Query(k, 1, n, 1));
            }
        }
    }
    return 0;
}
//set
/* 
 * @Author: lzyws739307453 
 * @Language: C++ 
 */
#include <bits/stdc++.h>
using namespace std;
int main() {
    char op;
    int n, m, k;
    while (~scanf("%d%d", &n, &m)) {
        set <int> spt;
        stack <int> S;
        set <int>::iterator l, r;
        spt.insert(0), spt.insert(n + 1);
        while (m--) {
            scanf(" %c", &op);
            if (op != 'Q') {
                if (op != 'R') {
                    scanf("%d", &k), S.push(k);
                    spt.insert(k);
                }
                else {
                    k = S.top(), S.pop();
                    spt.erase(k);
                }
            }
            else {
                scanf("%d", &k);
                if (spt.find(k) != spt.end())
                    printf("0\n");
                else {
                    l = r = spt.lower_bound(k);
                    printf("%d\n", *(r) - *(--l) - 1);
                }
            }
        }
    }
    return 0;
}
展开阅读全文
©️2020 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值