hdu 1540/POJ 2892 Tunnel Warfare 线段树区间合并

Posted yutingliuyl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 1540/POJ 2892 Tunnel Warfare 线段树区间合并相关的知识,希望对你有一定的参考价值。

Tunnel Warfare

                                                            Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
                                                                                                                       链接:hdu 1540        POJ 2892

 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

题意:

         给定N个点。点的编号是从1 ~ N,M次‘Q’或‘D或‘R’的操作,“D  x”表示的是破坏这个点x,“R”是修复之前破坏的点,“Q x”表示询问点x所在连续区间的长度。假定最初每一个点都是好的。

分析:

         首先对操作进行分析:能够把D操作看成把区间在点x出截断,R操作是把x左右【包含点x】进行合并。

每次合并两个区间 [a,b] , [c,d] 的时候,得到的新的区间[a,d] 的最大连续区间长度为:

        Len(a,d)  =  max{ Len(a,b),Len(c,d) ,End(b) + Begin(c) };  <==有一点分治的思想

         End(b)表示以b结尾的最大连续区间长度。Begin(c)表示以c开头的最大连续区间长度,这里我们能够知道,合并两个区间,并非简单的区间加减。而是还要保持一个以区间第一个元素開始的最大连续区间长度以及 以区间最后一个元素结尾的最大连续区间长度。

         那么。如今问题就比較好办了。线段树每一个节点包括三个信息。各自是以区间第一个元素開始的最大连续区间长度ln。以区间最后一个元素结尾的最大连续区间长度rn以及区间最大连续长度mn

接下来,便是Q操作了,询问点x 所在连续区间的长度。

在递归左儿子节点的时候,注意一下,x是否在以左儿子区间最后一个元素结尾长度为左儿子rn的范围里面,假设在,那么x所在的连续区间便有可能包括右儿子的一部分,递归右儿子的时候,同理!

/****************************>>>>HEADFILES<<<<****************************/
#include <set>
#include <map>
#include <list>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <algorithm>
using namespace std;
/****************************>>>>>DEFINE<<<<<*****************************/
#define fst             first
#define snd             second
#define root            1,N,1
#define lson            l,mid,rt<<1
#define rson            mid+1,r,rt<<1|1
#define PB(a)           push_back(a)
#define MP(a,b)         make_pair(a,b)
#define CASE(T)         for(scanf("%d",&T);T--;)
#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
//#pragma comment(linker, "/STACK:1024000000,1024000000")
typedef __int64         LL;
const int INF = 0x3f3f3f3f;
/****************************>>>>SEPARATOR<<<<****************************/
const int maxn = 50000 + 5;
int N, M;
struct Node
{
    int ln, rn, mn;
} segtree[maxn << 4];
inline void PushUp(const int& rt, const int& l, const int& r)
{
    segtree[rt].ln = segtree[rt << 1].ln;
    segtree[rt].rn = segtree[rt << 1 | 1].rn;
    segtree[rt].mn = max(segtree[rt << 1].rn + segtree[rt << 1 | 1].ln, 
                         max(segtree[rt << 1].mn, segtree[rt << 1 | 1].mn));
    int mid = (l + r) >> 1;
    if(segtree[rt << 1].mn == mid - l + 1) segtree[rt].ln += segtree[rt << 1 | 1].ln;
    if(segtree[rt << 1 | 1].mn == r - (mid + 1) + 1) segtree[rt].rn += segtree[rt << 1].rn;
}
void Build(int l, int r, int rt)
{
    segtree[rt].ln = segtree[rt].rn = segtree[rt].mn = r - l + 1;
    if(l == r)
    {
        return ;
    }
    int mid = (l + r) >> 1;
    Build(lson);
    Build(rson);
}
void Update(int pos, int val, int l, int r, int rt)
{
    if(l == r)
    {
        segtree[rt].ln = segtree[rt].rn = segtree[rt].mn = val;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)
        Update(pos, val, lson);
    else
        Update(pos, val, rson);
    PushUp(rt, l, r);
}
int Query(int pos, int l, int r, int rt)
{
    if(r - l + 1 == segtree[rt].mn || r  == l || segtree[rt].mn == 0)
    {
        return segtree[rt].mn;
    }
    int mid = (l + r) >> 1, ret = 0;
    if(pos <= mid)
    {
        if(pos >= mid - segtree[rt << 1].rn + 1)
            ret = Query(pos, lson) + Query(mid + 1, mid + 1, r, rt << 1 | 1);
        else ret = Query(pos, lson);
    }
    else
    {
        if(pos <= (mid + 1) + segtree[rt << 1 | 1].ln - 1)
            ret = Query(pos, rson) + Query(mid, l, mid, rt << 1);
        else ret = Query(pos, rson);
    }
    return ret;
}
int destroy[maxn];
int main()
{
    //FIN;
    while(~scanf("%d %d", &N, &M))
    {
        Build(root);
        char Op[5];
        int x, cnt = 0;
        for(int i = 0; i < M; i++)
        {
            scanf("%s", Op);
            if(Op[0] == 'Q')
            {
                scanf("%d", &x);
                printf("%d\n", Query(x, root));
            }
            else if(Op[0] == 'D')
            {
                scanf("%d", &x);
                Update(x, 0, root);
                destroy[cnt++] = x;
            }
            else
            {
                x = destroy[--cnt];
                Update(x, 1, root);
            }
        }
    }
}

一年之后,再写了一遍这个题目。感觉代码风格还是变化了一点。再贴个代码~ 

#include <bits/stdc++.h>

using namespace std;

const int MX = 5e4 + 5;

#define lch     (rt << 1)
#define rch     (rt << 1 | 1)

typedef pair<int, int> PII;

struct Seg {
    /**
      * status:
      * 0 -- Bad
      * 1 -- Good
      * -1 -- not cover
      * I: 节点相应区间两端点以及两短点的标号
      */
    int sum, status;
    PII I[2];
} seg[MX * 3];

int N, M, x;
char oper[5];

inline void pushUp(int rt) {
    if(seg[lch].status == seg[rch].status) seg[rt].status = seg[lch].status;
    else seg[rt].status = -1;

    if(seg[lch].status == 1 && seg[rch].status == 1) seg[rt].sum = seg[lch].sum + seg[rch].sum;
    else seg[rt].sum = 0;
}

void build(int l, int r, int rt) {
    int temp;
    if(l == r) {
        seg[rt].sum = seg[rt].status = 1;
        seg[rt].I[0] = make_pair(l, rt);
        seg[rt].I[1] = make_pair(r, rt);
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, lch);
    build(mid + 1, r, rch);
    pushUp(rt);
    seg[rt].I[0] = seg[lch].I[0];
    seg[rt].I[1] = seg[rch].I[1];
}

void update(int pos, int v, int l, int r, int rt) {
    if(l == r) {
        seg[rt].status = v;
        seg[rt].sum = (v == 1) ? 1 : 0;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) update(pos, v, l, mid, lch);
    else update(pos, v, mid + 1, r, rch);
    pushUp(rt);
}

int query_status(int pos, int l, int r, int rt) {
    if(l == r) return seg[rt].status;
    int mid = (l + r) >> 1;
    if(pos <= mid) return query_status(pos, l, mid, lch);
    else return query_status(pos, mid + 1, r, rch);
}

int query(int pos, int l, int r, int rt, int d) {
    if(seg[rt].status == 0) return 0;
    int mid = (l + r) >> 1, ret = 0;
    if(seg[rt].status == 1) {
        ret += seg[rt].sum;
        PII &lb = seg[rt].I[0], &ub = seg[rt].I[1];
        if(lb.first != 1 && seg[lb.second].status == 1 && d != 1)
            ret += query(lb.first - 1, 1, N, 1, -1);
        if(ub.first != N && seg[ub.second].status == 1 && d != -1)
            ret += query(ub.first + 1, 1, N, 1, 1);
        return ret;
    }
    if(pos <= mid) {
        ret += query(pos, l, mid, lch, d);
    } else {
        ret += query(pos, mid + 1, r, rch, d);
    }
    return ret;
}

int main() {
//    freopen("input.txt", "r", stdin);
    while(~scanf("%d %d", &N, &M)) {
        build(1, N, 1);
        stack<int> buf;
        while(M --) {
            scanf("%s", oper);
            if(oper[0] == 'D') {
                scanf("%d", &x); buf.push(x);
                update(x, 0, 1, N, 1);
            } else if(oper[0] == 'Q') {
                scanf("%d", &x);
                int k = query_status(x, 1, N, 1), ans = 0;
                if(k != 0) {
                    ans = query(x, 1, N, 1, 0);
                }
                printf("%d\n", ans);
            } else {
                if(buf.empty()) continue;
                x = buf.top(); buf.pop();
                update(x, 1, 1, N, 1);
            }
        }
    }
    return 0;
}


以上是关于hdu 1540/POJ 2892 Tunnel Warfare 线段树区间合并的主要内容,如果未能解决你的问题,请参考以下文章

hdu1540/poj2892 线段数区间合并

poj 2892 Tunnel Warfare(线段树)

poj 2892Tunnel Warfare 二分+树状数组

poj2892 Tunnel Warfare

POJ2892 Tunnel Warfare

POJ2892 Tunnel Warfare