首页 > 其他分享 >[lnsyoj336/luoguP2894/USACO08FEB]Hotel

[lnsyoj336/luoguP2894/USACO08FEB]Hotel

时间:2024-05-29 22:32:56浏览次数:18  
标签:子串 int Hotel tree len 区间 USACO08FEB lnsyoj336 llen

题意

原题链接
给定只包含\(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;
}

标签:子串,int,Hotel,tree,len,区间,USACO08FEB,lnsyoj336,llen
From: https://www.cnblogs.com/XiaoJuRuoUP/p/18221268/lnsyoj336_luoguP2894

相关文章

  • 洛谷题单指南-搜索-P2895 [USACO08FEB] Meteor Shower S
    原题链接:https://www.luogu.com.cn/problem/P2895题意解读:所谓安全格子,就是在所有流星坠落之后,没有被烧焦的格子,要计算从起点到这些格子任一一个的最短路径,BFS可以解决。解题思路:1、读取数据,先把所有流星坠落点以及周围被烧焦的格子进行标记,得到安全格子2、从起点开始BFS,每走......
  • P3565 [POI2014] HOT-Hotels
    题目描述:给定一棵树,在树上选\(3\)个点,要求两两距离相等,求方案数。数据范围:\(1\len\le5000\)\(1\lea,b\len\)思路:一开始我想的就是枚举两个点,然后处理第三个点。但是发现这样做非常的不正确,并且非常容易算重,所以我舍去了这种方式。但是在想这种做法的时候,我们会发现,......
  • P2895 [USACO08FEB] Meteor Shower S
    P2895[USACO08FEB]MeteorShowerS语言问题引发的惨案题目本身不难,简单的BFS,但是写出来明明思路感觉没有问题,却不是答案错就是爆队列。#include<iostream>#include<algorithm>#include<cstdio>#include<cstring>#include<queue>usingnamespacestd;structNode......
  • P3565 [POI2014] HOT-Hotels
    三倍经验:bzoj#3522P3565loj#2431加强版:bzoj#4543先看bzoj#3522这题。容易想到时间\(O(n^2)\),空间\(O(n^2)\)的树形dp。设\(dp_{1/2/3,u,i}\)表示以\(u\)为根的子树中所有以\(u\)为一端点,长度为\(i\)的路径中选\(1/2/3\)条路径的方案数(......
  • [USACO08FEB]meteor Shower S题解(bfs)
    题目描述贝茜听说一场特别的流星雨即将到来:这些流星会撞向地球,并摧毁它们所撞击的任何东西。她为自己的安全感到焦虑,发誓要找到一个安全的地方(一个永远不会被流星摧毁的地方)。如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。根据预......
  • [POI2014] HOT-Hotels 加强版
    [POI2014]HOT-Hotels题面翻译给定一棵树,在树上选\(3\)个点,要求两两距离相等,求方案数。题目描述Thereare\(n\)townsinByteotia,connectedwithonly\(n-1\)roads.Eachroaddirectlylinkstwotowns.Alltheroadshavethesamelengthandaretwoway.Itis......
  • hotel数据结构分析
           ......
  • 【题解】CF1854D Michael and Hotel
    交互题。考虑题意即为找到\(1\)所在内向基环树上的所有点。我们考虑我们怎么找到环上的点,我们考虑我们可以\(O(\logn)\)询问到一个环上的点,方法即为将\(k\)定为一个大数,然后二分点集。然后我们便可以在\(O(n\logn)\)的时间复杂度内找到所有环上的点(我们一会儿再讲怎......
  • P5904 [POI2014] HOT-Hotels 加强版
    自然的想法是枚举共同的交点,然后进行换根dp,复杂度可以做到\(\mathcalO(n^2)\),可以通过简单版,但是显然过不了\(10^5\)的数据,考虑进行优化。记\((x,y,z)\)为满足要求的点,即满足\(a=b+c\),树形dp原则是子树内的信息无后效性,尽量把子树内的信息合并在一起。所以\(a-b=c\),......
  • 洛谷 P2894 [USACO08FEB] Hotel G 题解
    题目链接P2894[USACO08FEB]HotelG-洛谷|计算机科学教育新生态(luogu.com.cn)分析考虑用线段树维护区间信息维护sum(最大连续空房间数)如何合并?sum1为max(sum2,sum3)(1的两个子区间)但我们发现若区间为100001(0表示空房间)sum1=4而max(sum2,sum3)=2所以再维护suml(从左开始的......