hdu 1540 Tunnel Warfare 【线段树 区间合并】

Tunnel Warfare

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

题目大意:
n个村庄,编号1~n,“D x”表示摧毁x村庄,“Q x”表示查询包括x的连续村庄个数,”R“表示回复最后一个被摧毁的村庄。

不止一组数据!!!
英语不好没看出来。。。

1.线段树+单点更新+区间合并

#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std;

const int MAXN = 50010;
struct node
{
    int l, r;            //区间左右边界
    int ll, rl, ml;      //ll:区间最左连续大小  rl:最右   ml:区间最大连续  
};
node tree[MAXN<<2];
int q[MAXN];

void build(int i, int l, int r)
{
    tree[i].l = l;
    tree[i].r = r;
    tree[i].ll = tree[i].rl = tree[i].ml = r - l + 1;
    if(l == r)
        return;
    int mid = l + r >> 1;
    build(i<<1, l, mid);
    build(i<<1|1, mid+1, r);
}
void update(int i, int t, int f)
{
    if(tree[i].l == tree[i].r)
    {
        tree[i].ll = tree[i].rl = tree[i].ml = f;
        return;
    }
    int mid = tree[i].l + tree[i].r >> 1;
    if(t <= mid)
        update(i<<1, t, f);
    else
        update(i<<1|1, t, f);

    tree[i].ml = max(max(tree[i<<1].ml, tree[i<<1|1].ml), tree[i<<1].rl + tree[i<<1|1].ll);
    tree[i].ll = tree[i<<1].ll;
    tree[i].rl = tree[i<<1|1].rl;

    if(tree[i<<1].ml == tree[i<<1].r - tree[i<<1].l + 1)       //左子区间满时,更新ll
        tree[i].ll += tree[i<<1|1].ll;
    if(tree[i<<1|1].ml == tree[i<<1|1].r - tree[i<<1|1].l + 1)  //右子区间满时,更新rl
        tree[i].rl += tree[i<<1].rl;
}
int query(int i, int t)
{
    if(tree[i].l == tree[i].r || tree[i].ml == 0 || tree[i].ml == tree[i].r - tree[i].l + 1)
        return tree[i].ml;

    //t恰好位于左右子区间之间连续段时,直接返回
    if(t >= tree[i<<1].r - tree[i<<1].rl + 1 && t <= tree[i<<1|1].l + tree[i<<1|1].ll - 1)
        return tree[i<<1].rl + tree[i<<1|1].ll;  

    int mid = tree[i].l + tree[i].r >> 1;
    if(t <= mid)
        return query(i<<1, t);
    else
        return query(i<<1|1, t);
}
int main(int argc, char const *argv[])
{
    int n, m;
    while(~scanf("%d%d", &n, &m))
    {
        getchar();
        int t;
        char ch;
        int top = -1;
        build(1, 1, n);
        while(m--)
        {
            ch = getchar();
            if(ch == 'D')
            {
                scanf("%d", &t);            
                q[++top] = t;
                update(1, t, 0);
            }
            else if(ch == 'Q')
            {
                scanf("%d", &t);
                printf("%d\n", query(1, t));
            }
            else
            {
                if(top != -1)
                    update(1, q[top--], 1);
            }
            getchar();
        }
    }
    return 0;
}

2.做题时还没写过区间合并,作死手写了个二叉树摸拟,死活不对,求大神指点。
思路:

  • 每次销毁,找到最小连续区间,标记断点并建立左右孩子。
  • 重建时总是重建最后一个,所以可以找到断点所在区间,清断断点并删除左右孩子。
#include <cstdio>
#include <stack>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 50010;
int q[MAXN];
int top;
struct node
{
    int l, r, d;                //l:区间左边界  r:区间右边界   d:断点  0表示无断点
    node *lc, *rc;              //d左边区间   d右边区间
};

//rt区间增加断点t
void del(node* rt, int t)       
{
    if(!(rt->d))                //无断点时插入断点
    {
        rt->d = t;
        node *p;
        if(rt->l < t)   
        {
            p = new node;
            p->l = rt->l;
            p->r = t - 1;
            p->d = 0;
            p->lc = p->rc = NULL;
            rt->lc = p;
        }
        if(t < rt->r)                   //存在左孩子
        {
            p = new node;
            p->l = t + 1;
            p->r = rt->r;
            p->d = 0;
            p->lc = p->rc = NULL;
            rt->rc = p;
        }
        return;
    }
    if(t == rt->d)                      //存在右孩子
    {
        top--;
        return;
    }

    if(t < rt->d)               //区间存在断点 分左右
        del(rt->lc, t);         
    else if(t > rt->d)
        del(rt->rc, t);
}

//查找包括t的连续区间
int query(node* rt, int t)
{
    if(!(rt->d))                        //连续区间返回r-l+1
        return rt->r - rt->l + 1;
    else
    {
        if(t < rt->d)                   //分左右
            return query(rt->lc, t);
        else if(t > rt->d)
            return query(rt->rc, t);
        else                            //恰好等于断点 返回0
            return 0;                   
    }
}

//恢复t
void rec(node* rt, int t)
{
    if(rt->d == t)                  //找到t断点 置0并去掉左右孩子
    {
        rt->d = 0;
        if(rt->lc)
        {
            free(rt->lc);
            rt->lc = NULL;
        }
        if(rt->rc)
        {
            free(rt->rc);
            rt->rc = NULL;
        }
    }
    else if(t < rt->d)
        rec(rt->lc, t);
    else
        rec(rt->rc, t);
}
int main()
{
    int n, m;
    node *head = new node;

    while(~scanf("%d%d", &n, &m))
    {
        getchar();
        head->l = 1;
        head->r = n;
        head->d = 0;
        head->lc= head->rc= NULL;
        int t;
        char ch;
        int top = -1;
        while(m--)
        {
            ch = getchar();
            if(ch == 'D')
            {
                scanf("%d", &t);            
                q[++top] = t;
                del(head, t);
            }
            else if(ch == 'Q')
            {
                scanf("%d", &t);
                printf("%d\n", query(head, t));
            }
            else
            {
                if(top != -1)
                    rec(head, q[top--]);
            }
            getchar();
        }

    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值