HDU1540 Tunnel Warfare(线段树 连续区间长度)

题意分析

操作:
1. 破坏某个点
2. 恢复某个点
3. 查询该点与相连的未被破坏的点的最长长度

使用线段树维护最长连续区间和,具体方法如下。

首先我们先增加两个域,llen,rlen
llen表示一个区间从最左端开始可用的且连续的最大长度
例如区间[1,5],覆盖情况为[0,0,0,1,1],llen = 3,从最左端有3格可以利用
区间[1,5],覆盖情况为[1,0,0,0,0],llen = 0,因为从最左端开始找不到1格可用的区间
rlen表示一个区间从最右端开始可用的且连续的最大长度
例如区间[1,5],覆盖情况为[1,0,1,0,0],rlen = 2,从最右端有2格可以利用
区间[1,5],覆盖情况为[0,0,0,0,1],rlen = 0,因为从最右端开始找不到1格可用的区间
对于一个区间,我们知道它左半区间的tlen,和右半区间的tlen,如果左半区间的tlen >= W ,那么我们一定能在左边找到(满足最左),所以可以深入到左半区间去确定该区间的具体位置
如果左端的不满足,那么我们要先考虑横跨两边的区间(因为要满足最左),因而记录的llen,rlen可以派上用场,一段横跨的区间,
那么是 左边区间rrlen + 右边区间llen ,如果满足的话,就是该区间了,它的位置也是可以确定的
如果横跨的区间不满足,那么就在右半区间找,如果右半区间的tlen >= W , 那么可以在右半区间找到,所以深入到右半区间去确定它的具体位置,否则的话,整个查询就失败了

可见查询是建立在tlen,llen,rlen这个信息之上的,而每次查询后其实伴随着修改,而且还有专门的修改操作,这些修改操作都会改变tlen,llen,rlen的值,所以在更新的时候是时刻维护这些信息

关于这3个信息的维护

当前区间的tlen = max{ 左半区间tlen , 右半区间tlen , 左半区间rlen+右半区间llen} (这个不难理解吧,取左右较大的那个,或者横跨中间的那个)

如果左半区间全部可以用: 当前区间llen = 左半区间llen(tlen) + 右半区间llen
左半区间部分能用: 当前区间llen = 左半区间llen

如果右半区间全部能用: 当前区间rlen = 右半区间rlen(tlen) + 左半区间rlen
右半区间部分能用: 当前区间rlen = 右半区间rlen

这样就全部维护好了

如何查询呢?
首先可以确定的是单点查询,但是我们还要查询出来与其相连长度为多少,如果要查询的这个点mid左边,肯定是要向左边查询的。
但是要考虑到,如果左边连续的部分比要查询 <= 要查询的pos,这说明pos在左边的连续区间内,就要查询他右边有多少连续区间,最后求和。

同理,如果要查询的部分在mid右边,那么肯定是要向右边查询的。
如果 右边连续的部分 >= pos ,这说明pos在右边连续的区间内,就要同时查询左边有多少连续区间,最后求和。

这里写图片描述

如果不明白就再看一下这张图片
图1 红色表示连续的区间,蓝色表示要查询的位置,此时要查询的pos明显不在连续的区间内,这时无论红色区间右边有多少连续区间,都是没有意义的,因为蓝色不和图中红色相交,这时候只需要查询蓝色位置连续长度即可。

图2 红色表示连续的区间,蓝色表示要查询的位置,此时要查询的pos明显在区间内,这是红色区间右边长度对最后答案是有影响的,就需要查询右侧的长度是多少,最后求和。

只不过具体实现的时候,要考虑左右而已,主题思想就是这样。

这样就完成了维护与查询

代码总览

#include<cstdio>
#include<stack>
#include<algorithm>
using namespace std;
const int nmax = 55005;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef struct{
    int l,r;
    int llen, rlen, tlen;
    int mid() {return (l+r) >> 1;}
} Tree;
Tree tree[nmax<<2];
int n,m;
void build(int l, int r,int rt){
    tree[rt].l = l, tree[rt].r = r;
    tree[rt].rlen = tree[rt].llen = tree[rt].tlen = r - l + 1;
    if(tree[rt].l == tree[rt].r) return;
    build(l,tree[rt].mid(), rt<<1);
    build(tree[rt].mid() + 1,r, rt<<1|1);
}
inline bool cmp(int rt){
    return (tree[rt].tlen == tree[rt].r - tree[rt].l + 1);
}
void pushup(int rt){
    tree[rt].tlen = max(tree[rt<<1].rlen + tree[rt<<1|1].llen, max(tree[rt<<1].tlen, tree[rt<<1|1].tlen));
    tree[rt].llen = (cmp(rt<<1)) ? (tree[rt<<1].tlen + tree[rt<<1|1].llen) : tree[rt<<1].llen ;
    tree[rt].rlen = (cmp(rt<<1|1)) ? (tree[rt<<1|1].tlen + tree[rt<<1].rlen) : tree[rt<<1|1].rlen;
}
void change(int type, int pos, int rt){
    if(tree[rt].l == pos && tree[rt].r == pos){
        tree[rt].rlen = tree[rt].llen = tree[rt].tlen = type;
        return;
    }
    if(pos <= tree[rt].mid()) change(type,pos,rt<<1);
    if(pos > tree[rt].mid()) change(type,pos,rt<<1|1);
    pushup(rt);
}
int query(int pos ,int rt){
    if(tree[rt].l == tree[rt].r || tree[rt].tlen == tree[rt].r - tree[rt].l + 1 || !tree[rt].tlen ) return tree[rt].tlen;
    if(pos <= tree[rt].mid()) {
        if(pos >= tree[rt<<1].r - tree[rt<<1].rlen + 1) return query(pos,rt<<1) + query(tree[rt].mid() + 1, rt<<1|1);
        else return query(pos,rt<<1);
    }else{
        if(pos <= tree[rt<<1|1].llen + tree[rt<<1|1].l - 1) return query(pos,rt<<1|1) + query(tree[rt].mid(), rt<<1);
        else return query(pos,rt<<1|1);
    }
}
int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        build(1,n,1);
        char op[2]; int pos;
        stack<int> s; while(!s.empty()) s.pop();
        for(int i = 0;i<m;++i){
            scanf(" %s",op);
            if(op[0] == 'D'){
                scanf("%d",&pos);
                s.push(pos);
                change(0,pos,1);
            }else if(op[0] == 'Q'){
                scanf("%d",&pos);
                printf("%d\n",query(pos,1));
            }else{
                if(s.size()){
                    pos = s.top(); s.pop();
                    change(1,pos,1);
                }
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值