HDU1540 Tunnel Warfare

该题目有点意思,不容易想到是线段树,但是用线段树还有2种方法(我了解到的),这里选一种稍稍麻烦的办法说一下。

题目中有三个操作,一个是毁坏一个城市,一个是查询,一个是修复所有的城市,询问则是询问该城市左右相连的城市最大数量(毁掉就不算相连了)。

由于长度的限制,n^2复杂度舍弃了,至少是nlogn也就是今天要说的线段树。

那么线段树节点存什么呢?

除了该节点的l,r以外(此题推荐将l,r写在结构体里,虽然本人习惯都是将l,r作为函数的参数不断传参实现)

还有三个区间

ls:区间内的最左区间

ms:区间内的最大区间

rs:区间内的最右区间

查询时除了注意落在对应区间内还要住注意左右区间的合并,对于更新操作也是如此。具体什么意思,代码注释写的很详细了。

代码如下:

#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<cmath>
#include<stdio.h>
using namespace std;
#define zuo root*2
#define you root*2+1
#define INF 0x3f3f3f3f
//#pragma warning(disable:4996)
int n,m,x,q[50010];//如果修复多个用q模拟队列存一下 

int max(int a,int b)
{
    if(a>b)
        return a;
    return b;
}

struct node {
    int l,r; 
    int ls,rs,ms;
}t[200050];

void build(int root,int l,int r)
{
    t[root].l=l;t[root].r=r;
    t[root].ls = t[root].rs = t[root].ms = r-l+1;
    if(l!=r)
    {
        int mid = (l + r)>>1;
        build(zuo, l, mid);
        build(you, mid + 1, r);
    }
}

void update(int root, int seat, int kind)
{
    if (t[root].l == t[root].r)
    {
        if(kind==1)//修复 
        t[root].ls = t[root].rs = t[root].ms = 1;
        else
        t[root].ls = t[root].rs = t[root].ms = 0;
        return;
    }
    //pushdown(root, r - l + 1);
    int mid = (t[root].l + t[root].r)>>1;
    if(seat<=mid)
        update(zuo,seat, kind);
    else
        update(you,seat, kind);
    t[root].ls=t[zuo].ls;
    if(t[zuo].ls == t[zuo].r-t[zuo].l+1)//左子树区间满的特殊情况  
        t[root].ls += t[you].ls;  
        
    t[root].rs=t[you].rs;
    if(t[you].rs == t[you].r-t[you].l+1)  
        t[root].rs += t[zuo].rs;
    
    t[root].ms=max(max(t[zuo].ms,t[you].ms),t[zuo].rs+t[you].ls);// 最大值取三个值中的最大值 
}


int query(int root, int seat)
{
    if(t[root].l == t[root].r || t[root].ms == 0 || t[root].ms == t[root].r-t[root].l+1)//到了叶节点或者该区间为空或满都不必继续走  
        return t[root].ms;//体现了ms损坏时赋值0的妙处 
    int mid = (t[root].l+t[root].r)>>1;  
    if(seat<=mid)  
    {  
        if(seat >= t[zuo].r - t[zuo].rs + 1)//如果seat在左子树的右区间,则要加上右子树的左区间,思想同更新的时候 
            return query(zuo,seat)+query(you,mid+1);  
        else  
            return query(zuo,seat); 
    }  
    else  
    {  
        if(seat<=t[you].l+t[you].ls-1)//同理  
            return query(zuo,mid)+query(you,seat); 
        else  
            return query(you,seat);  
    }  
}
//询问的话要在父节点就决定子节点的询问,即在父节点就要知道自己点的相关信息
//所以我第一次发现我的模板的不好处,此处是这样子写的,下面注释附上我原来的模板写法 (模板里面是加法的线段树) 
/*
int query(int root, int l, int r, int ql, int qr)
{
    if (l > qr || r < ql)
        return 0;
    pushdown(root, r - l + 1);
    if (l >= ql&& r <= qr)
        return t[root].val;
    int mid = (l + r) / 2;
    return query(zuo, l, mid, ql, qr) + query(you, mid + 1, r, ql, qr);
}*/
int main()  
{   
    char s[2];  
    while(~scanf("%d%d",&n,&m))  
    {  
        int top = 0;  
        build(1,1,n);  
        while(m--)  
        {  
            scanf("%s",s);  
            if(s[0] == 'D')  
            {  
                scanf("%d",&x);  
                q[top++] = x;  
                update(1,x,0);  
            }  
            else if(s[0] == 'Q')  
            {  
                scanf("%d",&x);  
                printf("%d\n",query(1,x));  
            }  
            else  
            {  
                if(x>0)  
                {  
                    x=q[--top];  
                    update(1,x,1);  
                }  
            }  
        }  
    }  
  
    return 0;  
}

 

posted @ 2018-04-27 03:11  fantastic123  阅读(125)  评论(0编辑  收藏  举报