【线段树】17.6.3 旅馆 题解

这里写图片描述
线段树维护区间最长连续1,区间前缀最长连续1,后缀最长连续1
在查询的时候看这个区间的最长长度是否>=d
如果>=d,则先看左子树是否>=d,若否看跨区间的是否>=d,若否再找右子树
再支持一下区间赋值即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string>
#include <iomanip>
#include <ctime>
#include <climits>
#include <cctype>
#include <algorithm>
#define clr(x) memset(x,0,sizeof(x))
#define LL long long
#define lch(i) ((i)<<1)
#define rch(i) ((i)<<1|1)

using namespace std;

const int maxn = 50050;
int n,m,range,l,w,len,index;

template <class T> inline void read(T &x) {
    int flag = 1; x = 0;
    char ch = getchar();
    while(ch <  '0' || ch >  '9') { if(ch == '-')  flag = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = (x<<1)+(x<<3)+ch-'0'; ch = getchar(); }
    x *= flag;
}

struct node {
    int l,r,mark,tlen,llen,rlen;
    int mid() { return (l+r)>>1; }
    int cal_len() { return r-l+1; }
    void updata_len() { tlen = llen = rlen = ( mark ? 0 : cal_len()); }
} t[maxn<<2];

void build(int l, int r, int rt) {
    t[rt].l = l, t[rt].r = r, 
    t[rt].tlen = t[rt].llen = t[rt].rlen = t[rt].cal_len(),
    t[rt].mark = 0;
    if(l == r) return ;
    int mid = t[rt].mid();
    build(l, mid, lch(rt));
    build(mid+1, r, rch(rt));
    return ;
}

int query(int w, int rt) {
    if(t[rt].l == t[rt].r && w == 1) return t[rt].l;
    if(t[rt].mark != -1) {
        t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark, t[rt].mark = -1;
        t[lch(rt)].updata_len(); t[rch(rt)].updata_len(); 
    }
    if(t[lch(rt)].tlen >= w) return query(w, lch(rt));
    else if(t[lch(rt)].rlen+t[rch(rt)].llen >= w) return (t[lch(rt)].r-t[lch(rt)].rlen+1);
    else if(t[rch(rt)].tlen >= w) return query(w, rch(rt));
    else return 0;
}

void updata(int l, int r, int val, int rt) {
    if(t[rt].l == l && t[rt].r == r) { t[rt].mark = val; t[rt].updata_len(); return ; }
    if(t[rt].mark != -1) {
        t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark, t[rt].mark = -1;
        t[lch(rt)].updata_len(), t[rch(rt)].updata_len(); 
    }
    int mid = t[rt].mid();
    if(l > mid) updata(l, r, val, rch(rt));
    else if(r <= mid) updata(l, r, val, lch(rt));
    else updata(l, mid, val, lch(rt)), updata(mid+1, r, val, rch(rt));
    int tmp = max(t[lch(rt)].tlen, t[rch(rt)].tlen);
    t[rt].tlen = max(tmp,t[lch(rt)].rlen+t[rch(rt)].llen),
    t[rt].llen = t[lch(rt)].llen, t[rt].rlen = t[rch(rt)].rlen;
    if(t[lch(rt)].tlen == t[lch(rt)].cal_len()) t[rt].llen += t[rch(rt)].llen;
    if(t[rch(rt)].tlen == t[rch(rt)].cal_len()) t[rt].rlen += t[lch(rt)].rlen;
    return ;
}

int main() {
    freopen("hotel.in","r",stdin);
    freopen("hotel.out","w",stdout);
    int n,m;
    read(n); read(m);
    build(1, n, 1);
    while(m--) {
        read(range);
        if(range&1) {
            read(w); index = query(w, 1);
            printf("%d\n",index);
            if(index) updata(index, index+w-1, 1, 1);
        }
        else read(l), read(len), updata(l, l+len-1, 0, 1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值