[lnsyoj336/luoguP2894/USACO08FEB]Hotel
题意
原题链接
给定只包含和的序列,支持两种操作:
- 查询中最靠左的连续个元素均为的子串,输出子串的左端点,并将这个子串的所有元素置为
- 将中以开始,长度为的子串的所有元素置为
初始时的所有值都为
sol
区间修改,区间查询,考虑线段树
每个节点需要记录个值:
- 区间长度(便于更新节点信息)
- 区间内最长的连续子串长度
- 区间内包含左端点的最长的连续子串长度
- 区间内包含右端点的最长的连续子串长度
- 懒惰标记(表示空标记,表示区间所有元素改为,表示区间所有元素改为)
PUSHUP操作
可以直接相加;可以取左右儿子的和左儿子的与右儿子的之和中的最大值(想一想,为什么)
对于:
- 若左儿子的与其等长,说明左儿子全部为,
- 否则说明左儿子中存在,
同理
代码如下
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操作(懒惰标记下传)
若为,则无需操作,同时不应下传,因为如果将其下传,其左右儿子原有的会被删除
若为,则将左右儿子的,,都应置为,因为区间内的所有元素都被修改为了,不存在
若为,则将左右儿子的,,都应置为其,因为区间内的所有元素都被修改为了,最大的连续子串即为其区间长度
代码见下
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操作
如果遍历到的区间只有一个元素,即,说明该区间就是满足要求的子串的左端点,直接返回即可
由于题目要求输出最靠左的满足要求的答案,因此应按照从左到右的顺序判断
- 若答案在左儿子中,即,则遍历左儿子
- 若答案在左儿子和右儿子的交界处,即,则答案为左儿子包含右端点的最长的连续的子串的左端点,即,直接返回即可
- 若答案在右儿子中,即,则遍历右儿子
代码如下
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);
}
主函数中需要注意的要点
执行操作时,可以先判断根节点的是否大于等于,若不满足,说明区间中不存在这样的子串,直接输出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;
}
分类:
题解 / 2024训练
标签:
数据结构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!