HDU1540 Tunnel Warfare

比较简单的线段树的区间合并(比旅馆的稍微要考虑的东西要多一点)。

先放代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn = 50010;
struct stuff{
    int l,r;        //左子,右子
    int mm,rm,lm;   //区间最大长度,区间左最大长度,区间右最大长度
}a[maxn<<2];
stack<int> stk;     //用来存爆点的栈
void up(int rt)     //向上更新
{
    a[rt].lm = a[rt<<1].lm;
    a[rt].rm = a[rt<<1|1].rm;
    a[rt].mm = max(a[rt<<1|1].lm+a[rt<<1].rm,max(a[rt<<1|1].mm,a[rt<<1].mm));
    if(a[rt<<1].lm == a[rt<<1].r-a[rt<<1].l+1)
        a[rt].lm+=a[rt<<1|1].lm;
    if(a[rt<<1|1].rm == a[rt<<1|1].r-a[rt<<1|1].l+1)
        a[rt].rm+=a[rt<<1].rm;


}
void build(int l,int r,int rt)  //建树
{
    a[rt].l=l;
    a[rt].r=r;
    if(l==r){
        a[rt].lm=a[rt].rm=a[rt].mm = 1;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    up(rt);
}
void update(int rt,int t,int X)     //更新单点
{
    if(a[rt].l==a[rt].r){
        a[rt].mm=a[rt].lm=a[rt].rm = X;
        return;
    }
    int m=(a[rt].l+a[rt].r)>>1;
    if(t<=m)
        update(rt<<1,t,X);
    else
        update(rt<<1|1,t,X);
    up(rt);
}
int getn(int rt,int t)              //查找
{
    if(a[rt].l==a[rt].r || a[rt].mm==0 || a[rt].mm==a[rt].r-a[rt].l+1)
        return a[rt].mm;
    int m=(a[rt].l+a[rt].r)>>1;
    //cout << m<<endl;
    if(t<=m){
        if(t>=a[rt<<1].r - a[rt<<1].rm + 1)
            return getn(rt<<1,t)+getn(rt<<1|1,m+1);
        else
            return getn(rt<<1,t);
    }
    else{
        if(t<=a[rt<<1|1].l + a[rt<<1|1].lm - 1)
            return getn(rt<<1|1,t)+getn(rt<<1,m);
        else
            return getn(rt<<1|1,t);
    }
}
int main()
{
    int n,m,cnt,num;
    while(~scanf("%d%d",&n,&m)){
        cnt=0;
        build(1,n,1);
        while(m--){
            char c;
            cin>>c;
            if(c=='D'){
                //cout << c <<endl;
                cin>>num;
                stk.push(num);
                update(1,num,0);
            }
            else if(c=='Q'){
                //cout << c<<endl;
                cin>>num;
                cout << getn(1,num) <<endl;
            }
            else{
                //cout << c<<endl;
                num=stk.top();
                stk.pop();
                update(1,num,1);
            }
        }
    }
    return 0;
}

build、up、update这三个函数都是不变的,唯一需要多考虑的就是getn函数(下面拆开看):

    if(a[rt].l==a[rt].r || a[rt].mm==0 || a[rt].mm==a[rt].r-a[rt].l+1)
        return a[rt].mm;

    这里当查找满足叶子节点、区间最大值=0、区间最大值=区间范围 时,就可以返回区间最大值了。(因为这里的返回不会对结果产生影响)。

if(t<=m){
        if(t>=a[rt<<1].r - a[rt<<1].rm + 1)
            return getn(rt<<1,t)+getn(rt<<1|1,m+1);
        else
            return getn(rt<<1,t);
    }

    这里我们拿向左走为例子:我们向左走,会有两种情况:

        一是右边的区间是否为可取不会有影响(例如1~3这个子树,我们要看2这个点的最长长度,但是3这个点已经爆掉了,即2<3-0+1,所以就不用管4~∞了)。

        二是右边区间会产生影响(即3没爆,2>3-3+1)。综上,我们在if(t<=m)内分了两种情况,向右走同理。

猜你喜欢

转载自blog.csdn.net/wangjunchengno2/article/details/79994697