[lnsyoj336/luoguP2894/USACO08FEB]Hotel

题意

原题链接
给定只包含\(0\)\(1\)的序列\(a\),支持两种操作:

  1. 查询\(a\)中最靠左的连续\(x\)个元素均为\(0\)的子串,输出子串的左端点,并将这个子串的所有元素置为\(1\)
  2. \(a\)中以\(x\)开始,长度为\(d\)的子串的所有元素置为\(0\)
    初始时\(a\)的所有值都为\(0\)

sol

区间修改,区间查询,考虑线段树
每个节点需要记录\(5\)个值:

  1. 区间长度(便于更新节点信息)\(sze\)
  2. 区间内最长的连续\(0\)子串长度 \(len\)
  3. 区间内包含左端点的最长的连续\(0\)子串长度 \(llen\)
  4. 区间内包含右端点的最长的连续\(0\)子串长度 \(rlen\)
  5. 懒惰标记(\(0\)表示空标记,\(1\)表示区间所有元素改为\(1\)\(2\)表示区间所有元素改为\(0\)) \(lazytag\)

PUSHUP操作

\(sze\)可以直接相加;\(len\)可以取左右儿子的\(len\)和左儿子的\(rlen\)与右儿子的\(llen\)之和中的最大值(想一想,为什么)
对于\(llen\)

  • 若左儿子的\(llen\)与其\(sze\)等长,说明左儿子全部为\(0\)\(llen=lson.llen + rson.llen\)
  • 否则说明左儿子中存在\(1\)\(llen=lson.llen\)
    \(rlen\)同理
    代码如下
void pushup(int u){
    tree[u].sze = tree[u << 1].sze + tree[u << 1 | 1].sze;
    tree[u].len = max(tree[u << 1].len, tree[u << 1 | 1].len);
    tree[u].len = max(tree[u].len, tree[u << 1].rlen + tree[u << 1 | 1].llen);
    if (tree[u << 1].sze == tree[u << 1].llen) tree[u].llen = tree[u << 1].len + tree[u << 1 | 1].llen;
    else tree[u].llen = tree[u << 1].llen;
    if (tree[u << 1 | 1].sze == tree[u << 1 | 1].rlen) tree[u].rlen = tree[u << 1].rlen + tree[u << 1 | 1].len;
    else tree[u].rlen = tree[u << 1 | 1].rlen;
}

PUSHDOWN操作(懒惰标记下传)

\(lazytag\)\(0\),则无需操作,同时不应下传,因为如果将其下传,其左右儿子原有的\(lazytag\)会被删除
\(lazytag\)\(1\),则将左右儿子的\(len\)\(rlen\)\(llen\)都应置为\(0\),因为区间内的所有元素都被修改为了\(1\),不存在\(0\)
\(lazytag\)\(2\),则将左右儿子的\(len\)\(rlen\)\(llen\)都应置为其\(sze\),因为区间内的所有元素都被修改为了\(0\),最大的连续子串即为其区间长度
代码见下

void pushdown(int u){
    if (!lazytag[u]) return ;
    if (lazytag[u] == 1) {
        tree[u << 1].len = tree[u << 1].rlen = tree[u << 1].llen = 0;
        tree[u << 1 | 1].len = tree[u << 1 | 1].rlen = tree[u << 1 | 1].llen = 0;
    }
    if (lazytag[u] == 2) {
        tree[u << 1].len = tree[u << 1].rlen = tree[u << 1].llen = tree[u << 1].sze;
        tree[u << 1 | 1].len = tree[u << 1 | 1].rlen = tree[u << 1 | 1].llen = tree[u << 1 | 1].sze;
    }
    lazytag[u << 1] = lazytag[u << 1 | 1] = lazytag[u];
    lazytag[u] = 0;
}

UPDATE操作

与正常线段树区间UPDATE操作一致,当被查询区间覆盖当前区间时,与PUSHDOWN操作相同
代码见下

void update(int u, int l, int r, int L, int R, int val){
    if (L <= l && r <= R){
        lazytag[u] = val;
        if (val == 1) tree[u].len = tree[u].rlen = tree[u].llen = 0;
        else if (val == 2) tree[u].len = tree[u].rlen = tree[u].llen = tree[u].sze;
        return ;
    }
    pushdown(u);
    int mid = l + r >> 1;
    if (L <= mid) update(u << 1, l, mid, L, R, val);
    if (R > mid) update(u << 1 | 1, mid + 1, r, L, R, val);
    pushup(u);
}

QUERY操作

如果遍历到的区间只有一个元素,即\(l=r\),说明该区间就是满足要求的子串的左端点,直接返回即可
由于题目要求输出最靠左的满足要求的答案,因此应按照从左到右的顺序判断

  1. 若答案在左儿子中,即\(lson.len\ge x\),则遍历左儿子
  2. 若答案在左儿子和右儿子的交界处,即\(lson.rlen + rson.llen \ge x\),则答案为左儿子包含右端点的最长的连续\(0\)的子串的左端点,即\(mid - lson.rlen + 1\),直接返回即可
  3. 若答案在右儿子中,即\(rson.len\ge x\),则遍历右儿子
    代码如下
int query(int u, int l, int r, int x){
    if (l == r) return l;
    pushdown(u);
    int mid = l + r >> 1;
    if (tree[u << 1].len >= x) return query(u << 1, l, mid, x);
    else if (tree[u << 1].rlen + tree[u << 1 | 1].llen >= x) return mid - tree[u << 1].rlen + 1;
    else return query(u << 1 | 1, mid + 1, r, x);
}

主函数中需要注意的要点

执行操作\(1\)时,可以先判断根节点的\(len\)是否大于等于\(x\),若不满足,说明区间中不存在这样的子串,直接输出0即可
否则可以证明此时一定有解,此时再去执行QUERY操作可以免去判断无解情况

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 50005;

struct Node{
    int sze, len, llen, rlen;
}tree[N * 4];
int lazytag[N * 4];
int n, m;

void pushup(int u){
    tree[u].sze = tree[u << 1].sze + tree[u << 1 | 1].sze;
    tree[u].len = max(tree[u << 1].len, tree[u << 1 | 1].len);
    tree[u].len = max(tree[u].len, tree[u << 1].rlen + tree[u << 1 | 1].llen);
    if (tree[u << 1].sze == tree[u << 1].llen) tree[u].llen = tree[u << 1].len + tree[u << 1 | 1].llen;
    else tree[u].llen = tree[u << 1].llen;
    if (tree[u << 1 | 1].sze == tree[u << 1 | 1].rlen) tree[u].rlen = tree[u << 1].rlen + tree[u << 1 | 1].len;
    else tree[u].rlen = tree[u << 1 | 1].rlen;
}

void pushdown(int u){
    if (!lazytag[u]) return ;
    if (lazytag[u] == 1) {
        tree[u << 1].len = tree[u << 1].rlen = tree[u << 1].llen = 0;
        tree[u << 1 | 1].len = tree[u << 1 | 1].rlen = tree[u << 1 | 1].llen = 0;
    }
    if (lazytag[u] == 2) {
        tree[u << 1].len = tree[u << 1].rlen = tree[u << 1].llen = tree[u << 1].sze;
        tree[u << 1 | 1].len = tree[u << 1 | 1].rlen = tree[u << 1 | 1].llen = tree[u << 1 | 1].sze;
    }
    lazytag[u << 1] = lazytag[u << 1 | 1] = lazytag[u];
    lazytag[u] = 0;
}

void build(int u, int l, int r){
    if (l == r){
        tree[u].len = tree[u].llen = tree[u].rlen = tree[u].sze = 1;
        return ;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void update(int u, int l, int r, int L, int R, int val){
    if (L <= l && r <= R){
        lazytag[u] = val;
        if (val == 1) tree[u].len = tree[u].rlen = tree[u].llen = 0;
        else if (val == 2) tree[u].len = tree[u].rlen = tree[u].llen = tree[u].sze;
        return ;
    }
    pushdown(u);
    int mid = l + r >> 1;
    if (L <= mid) update(u << 1, l, mid, L, R, val);
    if (R > mid) update(u << 1 | 1, mid + 1, r, L, R, val);
    pushup(u);
}

int query(int u, int l, int r, int x){
    if (l == r) return l;
    pushdown(u);
    int mid = l + r >> 1;
    if (tree[u << 1].len >= x) return query(u << 1, l, mid, x);
    else if (tree[u << 1].rlen + tree[u << 1 | 1].llen >= x) return mid - tree[u << 1].rlen + 1;
    else return query(u << 1 | 1, mid + 1, r, x);
}

int main(){
    scanf("%d%d", &n, &m);
    build(1, 1, n);
    while (m -- ){
        int op;
        scanf("%d", &op);
        if (op == 1){
            int x;
            scanf("%d", &x);
            if (tree[1].len < x) puts("0");
            else {
                int res = query(1, 1, n, x);
                printf("%d\n", res);
                if (res) update(1, 1, n, res, res + x - 1, 1);
            }
        }
        else {
            int a, b;
            scanf("%d%d", &a, &b);
            update(1, 1, n, a, a + b - 1, 2);
        }
    }
    return 0;
}
posted @ 2024-05-29 22:31  是一只小蒟蒻呀  阅读(21)  评论(0编辑  收藏  举报