Tunnel Warfare HDU - 1540(线段树的区间合并)

题意: 在一条直线上右连续的n个点,现在,给你m个操作,D  x   破坏点 x。Q   x 查询,点x所在的区间的最长的没被破坏的连续的点的个数。

思路:线段数的区间更新 ,节点维护区间的最长连续的点的个数,其中有两个难点,即PushUp函数,和 query(查询) 函数

具体操作即作用在注释里面都有写到。(不明白的可以评论,或者私我,有时间一定马上回复,谢谢支持!)

(query操作参考了kuangbindalao的博客,注释加上了一点我自己的理解,如有错误,欢迎指出)

#include "iostream"
#include "stack"
#include "algorithm"
using  namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const  int Max=50005;
int sum[Max<<2],lsum[Max<<2],rsum[Max<<2];//分别记录总区间,从左端点开始,从右端点开始的最长连续区间
void build(int l,int r,int rt)
{
    sum[rt]=lsum[rt]=rsum[rt]=r-l+1;//最初,区间的最长连续区间即本区间的长度
    if(l==r) return;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void PushUp(int rt,int ll,int lr)
{
    lsum[rt]=lsum[rt<<1];
    rsum[rt]=rsum[rt<<1|1];
    if(lsum[rt]==ll) lsum[rt]+=lsum[rt<<1|1];//如果左区间的最长连续区间与左区间等长,说明区间没有被隔开,那么总区间的
    if(rsum[rt]==lr) rsum[rt]+=rsum[rt<<1];//区间还要加上右区间的最长连续区间,对于右区间的来说也是如此
    sum[rt]=max(rsum[rt<<1]+lsum[rt<<1|1],max(sum[rt<<1],sum[rt<<1|1]));//最后,总区间的最长连续区间即为可能的三个区间的最大值
}
void update(int c,int x,int l,int r,int rt)
{
    if(l==r){
        sum[rt]=lsum[rt]=rsum[rt]=c?1:0;//单点更新,如果是删除操作,那么赋值为0,否则为1
        return;
    }
    int m=(l+r)>>1;
    if(x<=m) update(c,x,lson);
    else update(c,x,rson);
    PushUp(rt,m-l+1,r-m);//更新值
}
int query(int x,int l,int r,int rt)
{
    if(l==r||sum[rt]==0||sum[rt]==r-l+1)//如果已经到了叶子节点或者最长连续区间为0或者最长连续区间为区间的长都可以直接返回
        return sum[rt];
    int m=(l+r)>>1;
    if(x<=m){
        if(x>=m-rsum[rt<<1]+1)//因为t<=mid,看左子树,m-rsum[rt<<1]+1代表左子树右边连续区间的左边界值,如果t在左子树的右区间内
        return query(x,lson)+query(m+1,rson);//那么还要加上右边的一段区间
        else//如果不在左子树的右边界区间内,则只需要看左子树
            return query(x,lson);
    }
    else{
        if(x<=m+1+lsum[rt<<1|1]-1)//右子树同理
            return query(x,rson)+query(m,lson);
        else
            return query(x,rson);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    while(cin>>n>>m){
        stack<int> s;//用一个栈来记录删除的点
        if(!s.empty()) s.pop();
        build(1,n,1);
        char ch;
        int x;
        while(m--){
            cin>>ch;
            if(ch=='D'){
                cin>>x;
                s.push(x);
                update(0,x,1,n,1);
            }
            else if(ch=='R'){
                if(x>0){
                    x=s.top();
                    s.pop();
                    update(1,x,1,n,1);
                }
            }
            else{
                cin>>x;
                cout<<query(x,1,n,1)<<endl;
            }
        }
    }
    return  0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值