Tunnel Warfare 线段树 区间合并|最大最小值

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!

InputThe 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. 
OutputOutput 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
#include <cstdio>//法一:最大值最小值法,这个就是每一个点,如果这个点没有被摧毁,那就找到这个点最左边的和最右边的
#include <cstdlib>//最大-最小+1.这个就是这个最大连续长度。
#include <queue>//建树,很简单,主要就是query和update。
#include <algorithm>//这个地方的怎么去找一个包含一个数的一个区间的最大最小值呢?
#include <vector>//这个就是从上面往下面查询的过程中,就去找,如果是找最大值就去max,最小值就取min
#include <cstring>//这个要注意建树,这个区间的最大值的意思是,小于等于这个数的最大的被炸了的村庄,这个就说明,开始最大值为0,因为没有任何一个村庄被炸
#include <string>//区间的最小值,意思是大于等于这个数,被炸了的村庄的最小值,开始为n+1.因为没有村庄被炸。
#include <iostream>
#include <stack>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 100;
struct node
{
    int l, r;
    int mx, mn;
}tree[maxn*4];
int n;
void build(int id,int l,int r)
{
    tree[id].l = l;
    tree[id].r = r;
    if(l==r)
    {
        tree[id].mn = n+1;
        tree[id].mx = 0;
        return;
    }
    int mid = (l + r) >> 1;
    build(id << 1, l, mid);
    build(id << 1 | 1, mid + 1, r);
    tree[id].mx = max(tree[id << 1].mx, tree[id << 1 | 1].mx);
    tree[id].mn = min(tree[id << 1].mn, tree[id << 1 | 1].mn);
}

void update_max(int id,int x,int z)
{
    if(tree[id].l==tree[id].r)
    {
        tree[id].mx = z;
        return;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if(x<=mid) update_max(id << 1, x, z);
    if (x > mid) update_max(id << 1 | 1, x, z);
    tree[id].mx = max(tree[id << 1].mx, tree[id << 1 | 1].mx);
}

void update_min(int id,int x,int z)
{
    if(tree[id].l==tree[id].r)
    {
        tree[id].mn = z;
        return;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (x <= mid) update_min(id << 1, x, z);
    if (x > mid) update_min(id << 1 | 1, x, z);
    tree[id].mn = min(tree[id << 1].mn, tree[id << 1 | 1].mn);
}

int query_max(int id,int x,int y)
{
    int ans = 0;
    if(x<=tree[id].l&&y>=tree[id].r)
    {
        return tree[id].mx;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (x <= mid) ans = max(ans, query_max(id << 1, x, y));
    if (y > mid) ans = max(ans, query_max(id << 1 | 1, x, y));
    return ans;
}

int query_min(int id,int x,int y)
{
    int ans = inf;
    if(x<=tree[id].l&&y>=tree[id].r)
    {
        return tree[id].mn;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (x <= mid) ans = min(ans, query_min(id << 1, x, y));
    if (y > mid) ans = min(ans, query_min(id << 1 | 1, x, y));
    return ans;
}

int main()
{

    int m, x;
    while(cin >> n >> m)
    {
        stack<int>sta;
        build(1, 1, n);
        while(m--)
        {
            char s[10];
            scanf("%s", s);
            if(s[0]==‘D‘)
            {
                cin >> x;
                update_max(1, x, x);
                update_min(1, x, x);
                sta.push(x);
            }
            if(s[0]==‘R‘)
            {
                int y = sta.top(); sta.pop();
                update_max(1, y, 0);
                update_min(1, y, n + 1);
            }
            if(s[0]==‘Q‘)
            {
                cin >> x;
                int L = query_min(1, x, n + 1);
                int R = query_max(1, 0, x);
                //printf("%d %d\n", L, R);
                if (L == R) printf("0\n");
                else printf("%d\n", L - R - 1);
            }
        }
    }

    return 0;
}
//法二:区间合并,这个应该更好懂一点,就是维护一下一个区间的前缀后缀长度
//这个更新应该比较简单,
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <iostream>
#include <stack>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 100;
struct node
{
    int l, r, len;
    int max_pre, max_last;
}tree[maxn*4];

void push_up(int id)
{
    tree[id].max_pre = tree[id << 1].max_pre;
    tree[id].max_last = tree[id << 1 | 1].max_last;
    if (tree[id << 1].max_pre == tree[id << 1].len)
    {
        tree[id].max_pre += tree[id << 1 | 1].max_pre;
    }
    if(tree[id<<1|1].max_last==tree[id<<1|1].len)
    {
        tree[id].max_last += tree[id << 1].max_last;
    }
}

void build(int id,int l,int r)
{
    tree[id].l = l;
    tree[id].r = r;
    tree[id].len = r - l + 1;
    if(l==r)
    {
        tree[id].max_pre = tree[id].max_last = 1;
        return;
    }
    int mid = (l + r) >> 1;
    build(id << 1, l, mid);
    build(id << 1 | 1, mid + 1, r);
    push_up(id);
}

void update(int id,int x,int z)
{
    if(tree[id].l==tree[id].r)
    {
        tree[id].max_pre = z;
        tree[id].max_last = z;
        return;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (x <= mid) update(id << 1, x, z);
    else update(id << 1 | 1, x, z);
    push_up(id);
}

int query_pre(int id,int x,int y)
{
    int ans = 0, res = 0;
    if(x<=tree[id].l&&y>=tree[id].r)
    {
        //printf("tree[%d].max_pre=%d\n", id, tree[id].max_pre);
        return tree[id].max_pre;
    }
    //printf("id=%d x=%d y=%d\n", id, x, y);
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (x <= mid) ans=query_pre(id << 1, x, y);
    if (y > mid) res=query_pre(id << 1 | 1, x, y);
    //printf("id=%d ans=%d res=%d mid=%d\n",id, ans, res,mid);
    if (ans >= mid - x + 1)
    {
        //printf("tree[%d].max_pre=%d mid=%d x=%d\n",id, tree[id].max_pre, mid, x);
        ans += res;
    }
    return ans;
}

int query_last(int id,int x,int y)
{
    int ans = 0, res = 0;
    if (x <= tree[id].l&&y >= tree[id].r)
    {
        //printf("tree[%d].last=%d\n", id, tree[id].max_last);
        return tree[id].max_last;
    }
    //printf("id=%d x=%d y=%d\n", id, x, y);
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (x <= mid) ans = query_last(id << 1, x, y);
    if (y > mid) res = query_last(id << 1 | 1, x, y);
    //printf("id=%d mid=%d ans=%d res=%d\n", id, mid,ans, res);
    if (res >= y-mid)
    {
        //printf("tree[%d].max_last=%d  mid=%d x=%d\n",id,tree[id].max_last, mid, x);
        res += ans;
    }
    return res;
}

int main()
{
    int n, m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        stack<int>sta;
        build(1, 1, n);
        while(m--)
        {
            char s[10];
            scanf("%s", s);
            if(s[0]==‘D‘)
            {
                int x;
                cin >> x;
                update(1, x, 0);
                sta.push(x);
            }
            if(s[0]==‘R‘)
            {
                int y = sta.top(); sta.pop();
                update(1, y, 1);
            }
            if(s[0]==‘Q‘)
            {
                int x;
                cin >> x;
                int ans = query_pre(1, x, n);
                ans += query_last(1, 1, x);
                if(ans) printf("%d\n", ans-1);
                else printf("0\n");
            }
        }
    }
    return 0;
}
/*
7 10
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4
Q 3
 */


原文地址:https://www.cnblogs.com/EchoZQN/p/10802782.html

时间: 2024-10-14 01:18:20

Tunnel Warfare 线段树 区间合并|最大最小值的相关文章

HDU 1540 Tunnel Warfare 线段树区间合并

Tunnel Warfare 题意:D代表破坏村庄,R代表修复最后被破坏的那个村庄,Q代表询问包括x在内的最大连续区间是多少 思路:一个节点的最大连续区间由(左儿子的最大的连续区间,右儿子的最大连续区间,左儿子的最大连续右区间+右儿子的最大连续左区间)决定 所以线段树的节点应该维护当前节点的最大连续左区间,最大连续右区间,和最大连续区间. 注意更新的时候,如果左儿子全满,父亲节点的左连续区间还要加上右儿子的左区间.反之同理. 查询的时候,可以剪枝,如果是叶子,或为空,或满,则不用往下查询. 查询

HDU 1540 Tunnel Warfare(线段树 区间合并 最大连续区间)

题意  有n个连在一起的地道  接下来有m个操作  D x 炸掉x号地道  炸掉后x所在的区间就不连续了  Q x 查询输出包括x的最大连续区间长度   R修复最后一个被炸的地道  注意输入R时可能并没有需要修复的地道 线段树的区间合并问题  线段树要维护3个信息 len  对应区间的最大连续长度 ll  对应区间最左端的一段连续长度 lr  对应区间最右端的一段连续长度 那么双亲节点的这些信息和孩子节点有什么关系呢  容易发现 双亲的len 是 左孩子的len 右孩子的len  左孩子的右端长

hdu 1540 Tunnel Warfare 线段树 区间合并

题意: 三个操作符 D x:摧毁第x个隧道 R x:修复上一个被摧毁的隧道,将摧毁的隧道入栈,修复就出栈 Q x:查询x所在的最长未摧毁隧道的区间长度. 1.如果当前区间全是未摧毁隧道,返回长度 2.如果在坐儿子的右区间或右儿子的左区间,返回这两个区间长度和 3.继续递归 #include <bits/stdc++.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 using namespace std;

Tunnel Warfare HDU 1540 区间合并+最大最小值

Tunnel Warfare HDU 1540 区间合并+最大最小值 题意 D x是破坏这个点,Q x是表示查询以x所在的最长的连续的点的个数,R是恢复上一次破坏的点. 题解思路 参考的大佬博客 这里巧妙使用了最大值最小值来进行区间的查找.上一行是大佬的详细题解,真的很妙啊. 代码实现 #include<cstdio> #include<cstring> #include<algorithm> #include<stack> #define ls (rt&l

HDU 1540 &amp;&amp; POJ 2892 Tunnel Warfare (线段树,区间合并).

~~~~ 第一次遇到线段树合并的题,又被律爷教做人.TAT. ~~~~ 线段树的题意都很好理解吧.. 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1540 http://poj.org/problem?id=2892 ~~~~ 我的代码:200ms #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #defin

HDU 1540 Tunnel Warfare(线段树单点更新+区间合并)

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 vill

hdu 1540 Tunnel Warfare(线段树)

题目链接:hdu 1540 Tunnel Warfare 题目大意:有连续的N个城镇,三种操作: D x:第x城镇被破坏 Q x:插叙第x城镇所在联通块有多少个城镇没有被破坏 R:修复最后一个被破坏的城镇 解题思路:线段树区间合并,每个城镇看成一个叶子节点,用一个vector记录破坏顺序.对于查询来说,每次只要判断是否在mid?R[lson(u)],mid+L[rson(u)]之间即可,否则即递归查询左右子树. #include <cstdio> #include <cstring>

POJ 3667 Hotel 【线段树 区间合并 + Lazy-tag】

Hotel Time Limit: 3000MS Memory Limit: 65536K 链接:POJ 3667   Description The cows are journeying north to ThunderBay in Canada to gain cultural enrichment and enjoy a vacation on the sunnyshores of Lake Superior. Bessie, ever the competent travel agen

hdu 3308(线段树区间合并)

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6069    Accepted Submission(s): 2635 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index