POJ 3667(线段树找连续m个点区间的最左端)

本文解析了一道关于线段树区间合并的经典算法题,通过逆向思维将问题转化为求最左连续空位的问题,详细介绍了如何维护最左、最右及最长连续状态,以及查询算法的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://poj.org/problem?id=3667

 

题目大意:两个操作,1 x表示找出最左连着的x个空位置,并占满,输出最左端,2 x y表示清空x到y位置

 

题目思路:逆向思维,空位置用1,占了的位置用0。然后就是基础的线段树区间合并,跟炸地道那题的x所在1长度方法是一样的。维护最左连续1,最右连续1和最长连续1。维护方法跟那题一样,可以看之前的博客。然后这题的还有个难点在查询。当整个区间的连续1个数都小于x时返回0。否则看左子树是否有这么多0,有的话继续搜左子树,如果左子树的最右连续1+右字数最左连续1是否满足,满足的话直接返回,否则搜右子树。

 

以下是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 5e4+5;
const int MOD = 1e9+7;
struct node{
    int l,r,lm,rm,mm,mark;
}a[MAXN<<2];
void build(int rt,int l,int r){
    a[rt].l=l,a[rt].r=r;
    a[rt].lm=a[rt].rm=a[rt].mm=r-l+1;
    a[rt].mark=-1;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
}
void spread(int rt){
    if(a[rt].mark!=-1){
        a[rt<<1].lm=a[rt<<1].rm=a[rt<<1].mm=a[rt].mark*(a[rt<<1].r-a[rt<<1].l+1);
        a[rt<<1|1].lm=a[rt<<1|1].rm=a[rt<<1|1].mm=a[rt].mark*(a[rt<<1|1].r-a[rt<<1|1].l+1);
        a[rt<<1].mark=a[rt<<1|1].mark=a[rt].mark;
        a[rt].mark=-1;
    }
}
void update(int rt,int l,int r,int val){
    if(l<=a[rt].l&&a[rt].r<=r){
        a[rt].lm=a[rt].rm=a[rt].mm=val*(a[rt].r-a[rt].l+1);
        a[rt].mark=val;
        return;
    }
    spread(rt);
    int mid=(a[rt].l+a[rt].r)>>1;
    if(l<=mid)update(rt<<1,l,r,val);
    if(r>mid)update(rt<<1|1,l,r,val);
    a[rt].lm=a[rt<<1].lm;
    a[rt].rm=a[rt<<1|1].rm;
    a[rt].mm=max(a[rt<<1].mm,a[rt<<1|1].mm);
    a[rt].mm=max(a[rt].mm,a[rt<<1].rm+a[rt<<1|1].lm);
    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;
}
int query(int rt,int x){
    spread(rt);
    if(a[rt].lm==x){
        return a[rt].l;
    }
    if(a[rt<<1].mm>=x)return query(rt<<1,x);
    else if(a[rt<<1].rm+a[rt<<1|1].lm>=x)return a[rt<<1].r-a[rt<<1].rm+1;
    else return query(rt<<1|1,x);
}
int main(){
    int n,m,p,x,y;
    while(~scanf("%d%d",&n,&m)){
        build(1,1,n);
        while(m--){
            scanf("%d",&p);
            if(p==1){
                scanf("%d",&x);
                if(a[1].mm<x){
                    printf("0\n");
                    continue;
                }
                int l=query(1,x);
                update(1,l,l+x-1,0);
                printf("%d\n",l);
            }
            else{
                scanf("%d%d",&x,&y);
                update(1,x,x+y-1,1);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值