「刷题记录」P7476 「C.E.L.U-02」苦涩
题目背景#
回想起自己的过往的人生, 觉得心中充满了苦涩。如果人生能再来一次,我一定会少做一些傻事,少真香几次,然后大胆地去追寻自己的爱。可惜没有这样一个机会了。
题目描述#
在 的梦中,他看到自己过去的记忆正在不断浮现在自己脑中。这些记忆带给他的是满满的苦涩。他想要强行忘记一些来减轻自己的苦涩。
的脑中可以被分成 个片区,每个片区相当于一个存放记忆的可重集,初始为空。他将进行 次这三种操作:
操作 :区间 的片区中都浮现了一个苦涩值为 的记忆。
操作 : 开始清理 片区的记忆。如果一个片区 且 中苦涩值最大的记忆与 片区中苦涩值最大的记忆相等,则将这个苦涩值最大的记忆忘记。如果在同一个片区有多个相同的苦涩值最大的记忆,则只忘记一个。如果这些片区内没有记忆,则无视。
操作 : 想知道, 片区中苦涩值最大的记忆的苦涩值是多少,如果不存在,输出 。
输入格式#
第一行两个数,。
接下来 行,第一个数代表操作种类 ,对于操作 ,有三个数 ,对于操作 或 ,有两个数 。
输出格式#
对于每个操作 输出一行,代表答案。
样例 #1#
样例输入 #1#
5 4
1 1 3 2
1 2 4 3
2 3 3
3 1 3
样例输出 #1#
3
样例 #2#
样例输入 #2#
6 6
1 1 6 2
1 3 3 2
1 3 4 3
2 3 4
3 3 3
3 4 4
样例输出 #2#
2
2
提示#
样例解释#
样例解释一#
下面为各操作之后 的大脑的状态:
第一次操作:
第二次操作:
第三次操作:
第四次操作询问 区间 的最大值,所以答案是 。
样例解释二#
下面为各操作之后 的大脑的状态:
第一次操作:
第二次操作:
第三次操作:
第四次操作:
第五次操作询问 的最大值,所以答案是 。
第六次操作询问 的最大值,所以答案是 。
数据范围#
Subtask | n | m | 特殊性质 |
---|---|---|---|
没有操作 2 | |||
操作 2 中 | |||
操作 2 中 | |||
对于 的数据,
思路#
一共有三个操作,别看他描述的花里胡哨的,其实就是区间加,区间删除,区间查询。
但是,由于区间加的操作,它的叶节点存储的不止有一个数字,而是一个数列,所以他和普通的线段树还不同,它的节点还要再套点其他东西
那么,我们要套什么呢?
它不是还要查询最大值吗,那我们就套一个堆
又有问题了,有区间操作,怎么下传懒标?将堆与孩子的堆进行合并——左偏树,想到这里,恭喜你,掉坑里了
左偏树合并是将整个堆都合并过去,他又不只有一个孩子,它和一个孩子合并了,那其他孩子呢?所以左偏树不行
既然传懒标很麻烦,那我们就不穿懒标了呗,这里介绍一个小优化——标记永久化
什么意思呢,就是标记不下放,永远留在当前位置
我们用堆来记录懒标,但是本题中标记永久化的不彻底,还有一种情况需要下传懒标,当堆顶元素与要删除的元素相等时,我们要下放懒标记
代码#
定义线段树:
struct tree {
int len;// 代表区间长度
ll maxx;// 代表这个区间的最大值
priority_queue<ll> q;// 大根堆记录并维护懒标
}t[N << 4];
建树操作:
void build(int cur, int l, int r) {// 建树
t[cur].len = r - l + 1;// 求len
t[cur].maxx = -1;// 求max
t[cur].q.push(-1);// -1压堆底
if(l == r) return ;// 到达叶子节点
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
这些与普通线段树没什么差异
接下来就是标记永久化
void addlazy(int cur, ll v) {// 永久化懒标记
t[cur].q.push(v);// 插入懒标记
t[cur].maxx = t[cur].maxx > v ? t[cur].maxx : v;
// 将max值与懒标记比大小
}
更新操作
void pushup(int cur) {
t[cur].maxx = max(max(t[ls].maxx, t[rs].maxx), t[cur].q.top());
// 将左右孩子的最大值与懒标记的最大值比大小
}
插入操作
void insert(int cur, int l, int r, int ql, int qr, ll k) {// 插入
if(ql <= l && r <= qr) {
addlazy(cur, k);// 永久化标记
return ;
}
int mid = (l + r) >> 1;
if(ql <= mid) insert(ls, l, mid, ql, qr, k);
if(qr > mid) insert(rs, mid + 1, r, ql, qr, k);
pushup(cur);// pushup更新
}
查询操作
ll ask(int cur, int l, int r, int ql, int qr) {// 查询最大苦涩值
if(ql <= l && r <= qr) {
return t[cur].maxx;// 返回这个区间的最大苦涩值
}
ll ans = t[cur].q.top();// 懒标记的最大值
int mid = (l + r) >> 1;
if(ql <= mid) ans = max(ans, ask(ls, l, mid, ql, qr));
// 左子树的最大苦涩值
if(qr > mid) ans = max(ans, ask(rs, mid + 1, r, ql, qr));
// 右子树的最大苦涩值
return ans;
}
删除操作
void del(int cur, int l, int r, int ql, int qr, ll k) {
if(ql <= l && r <= qr && t[cur].maxx < k) return ;
// 如果这个区间的最大值都比不过k,那就没有要删除的
if(t[cur].q.top() == k) {// 最大的懒标记与k相等
t[cur].q.pop();// 弹出
pushdown(cur, l, r, ql, qr, k);// 下放懒标记
if(l == r) {// 到达叶子节点,修改最大值
t[cur].maxx = t[cur].q.top();
//叶结点的最大值就是懒标最大值
}
else pushup(cur);//更新最大值
return ;
}
int mid = (l + r) >> 1;
if(ql <= mid) del(ls, l, mid, ql, qr, k);
// 在左子树中删除
if(qr > mid) del(rs, mid + 1, r, ql, qr, k);
// 在右子树中删除
pushup(cur);
}
完整代码
#include <iostream>
#include <cstdio>
#include <queue>
#define ls (cur << 1)
#define rs (cur << 1 | 1)
typedef long long ll;
using namespace std;
const int N = 2e5 + 5;
int n, m;
struct tree {
int len;// 代表区间长度
ll maxx;// 代表这个区间的最大值
priority_queue<ll> q;// 大根堆记录并维护懒标
}t[N << 4];
inline ll read() {
ll x = 0;
int fg = 0;
char ch = getchar();
while(ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
void build(int cur, int l, int r) {// 建树
t[cur].len = r - l + 1;// 求len
t[cur].maxx = -1;// 求max
t[cur].q.push(-1);// -1压堆底
if(l == r) return ;// 到达叶子节点
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
void addlazy(int cur, ll v) {// 永久化懒标记
t[cur].q.push(v);// 插入懒标记
t[cur].maxx = t[cur].maxx > v ? t[cur].maxx : v;
// 将max值与懒标记比大小
}
void pushup(int cur) {
t[cur].maxx = max(max(t[ls].maxx, t[rs].maxx), t[cur].q.top());
// 将左右孩子的最大值与懒标记的最大值比大小
}
void pushdown(int cur, int l, int r, int ql, int qr, ll k) {
// 把懒标记下放到[ql,qr]以外的其他区间中
if(ql <= l && r <= qr) return ;// 永久化标记
int mid = (l + r) >> 1;
if(ql > mid) {// 全在右子树上
addlazy(ls, k);// 永久化标记
pushdown(rs, mid + 1, r, ql, qr, k);// 下放标记
}
else {
if(qr <= mid) {// 全在左子树上
addlazy(rs, k);// 永久化标记
pushdown(ls, l, mid, ql, qr, k);// 下放标记
}
else {// 跨左右子树
pushdown(ls, l, mid, ql, qr, k);
pushdown(rs, mid + 1, r, ql, qr, k);
}
}
pushup(cur);
}
void insert(int cur, int l, int r, int ql, int qr, ll k) {// 插入
if(ql <= l && r <= qr) {
addlazy(cur, k);// 永久化标记
return ;
}
int mid = (l + r) >> 1;
if(ql <= mid) insert(ls, l, mid, ql, qr, k);
if(qr > mid) insert(rs, mid + 1, r, ql, qr, k);
pushup(cur);// pushup更新
}
void del(int cur, int l, int r, int ql, int qr, ll k) {
if(ql <= l && r <= qr && t[cur].maxx < k) return ;
// 如果这个区间的最大值都比不过k,那就没有要删除的
if(t[cur].q.top() == k) {// 最大的懒标记与k相等
t[cur].q.pop();// 弹出
pushdown(cur, l, r, ql, qr, k);// 下放懒标记
if(l == r) {// 到达叶子节点,修改最大值
t[cur].maxx = t[cur].q.top();
//叶结点的最大值就是懒标最大值
}
else pushup(cur);//更新最大值
return ;
}
int mid = (l + r) >> 1;
if(ql <= mid) del(ls, l, mid, ql, qr, k);
// 在左子树中删除
if(qr > mid) del(rs, mid + 1, r, ql, qr, k);
// 在右子树中删除
pushup(cur);
}
ll ask(int cur, int l, int r, int ql, int qr) {// 查询最大苦涩值
if(ql <= l && r <= qr) {
return t[cur].maxx;// 返回这个区间的最大苦涩值
}
ll ans = t[cur].q.top();// 懒标记的最大值
int mid = (l + r) >> 1;
if(ql <= mid) ans = max(ans, ask(ls, l, mid, ql, qr));
// 左子树的最大苦涩值
if(qr > mid) ans = max(ans, ask(rs, mid + 1, r, ql, qr));
// 右子树的最大苦涩值
return ans;
}
int main() {
t[0].maxx = -1;
t[0].q.push(-1);
n = read();
m = read();
build(1, 1, n);
for(int i = 1; i <= m; ++i) {
int op = read();
int l, r, k;
if(op == 1) {
l = read(), r = read(), k = read();
insert(1, 1, n, l, r, k);// 插入节点
}
if(op == 2) {
l = read(), r = read();
k = ask(1, 1, n, l, r);// 找最大苦涩值
if(k != -1) del(1, 1, n, l, r, k);
// 如果有最大苦涩值,删除
}
if(op == 3) {
l = read(), r = read();
printf("%lld\n", ask(1, 1, n, l, r));// 找最大苦涩值
}
}
return 0;
}
作者:yifan0305
出处:https://www.cnblogs.com/yifan0305/p/16560644.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
转载时还请标明出处哟!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】