ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P7476 「C.E.L.U-02」苦涩

2022-08-08 09:31:22  阅读:151  来源: 互联网

标签:02 maxx qr cur int mid P7476 苦涩 ql


「C.E.L.U-02」苦涩

题目背景

回想起自己的过往的人生,YQH 觉得心中充满了苦涩。如果人生能再来一次,我一定会少做一些傻事,少真香几次,然后大胆地去追寻自己的爱。可惜没有这样一个机会了。

题目描述

在 YQH 的梦中,他看到自己过去的记忆正在不断浮现在自己脑中。这些记忆带给他的是满满的苦涩。他想要强行忘记一些来减轻自己的苦涩。
YQH 的脑中可以被分成 \(n\) 个片区,每个片区相当于一个存放记忆的可重集,初始为空。他将进行 \(m\) 次这三种操作:
操作 1:区间 \(l\sim r\) 的片区中都浮现了一个苦涩值为 \(k\) 的记忆。
操作 2:YQH 开始清理 \(l\sim r\) 片区的记忆。如果一个片区 \(k\in[l,r]\) 且 \(k\) 中苦涩值最大的记忆与 \(l\sim r\) 片区中苦涩值最大的记忆相等,则将这个苦涩值最大的记忆忘记。如果在同一个片区有多个相同的苦涩值最大的记忆,则只忘记一个。如果这些片区内没有记忆,则无视。
操作 3:YQH 想知道,\(l\sim r\) 片区中苦涩值最大的记忆的苦涩值是多少,如果不存在,输出-1

输入格式

第一行两个数,\(n,m\)。
接下来 \(m\) 行,第一个数代表操作种类 \(op\),对于操作 1,有三个数 \(l,r,k\),对于操作 2 或 3,有两个数 \(l,r\)。

输出格式

对于每个操作 3 输出一行,代表答案。

样例 #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

提示

样例解释

样例解释一

下面为各操作之后 YQH 的大脑的状态:
第一次操作:\(\{2\},\{2\},\{2\},\varnothing,\varnothing\)
第二次操作:\(\{2\},\{2,3\},\{2,3\},\{3\},\varnothing\)
第三次操作:\(\{2\},\{2,3\},\{2\},\{3\},\varnothing\)
第四次操作询问 区间 \(1\sim 3\) 的最大值,所以答案是 \(3\)。

样例解释二

下面为各操作之后 YQH 的大脑的状态:
第一次操作:\(\{2\},\{2\},\{2\},\{2\},\{2\},\{2\}\)
第二次操作:\(\{2\},\{2\},\{2,2\},\{2\},\{2\},\{2\}\)
第三次操作:\(\{2\},\{2\},\{2,2,3\},\{2,3\},\{2\},\{2\}\)
第四次操作:\(\{2\},\{2\},\{2,2\},\{2\},\{2\},\{2\}\)
第五次操作询问 \(3\) 的最大值,所以答案是 \(2\)。
第六次操作询问 \(4\) 的最大值,所以答案是 \(2\)。

数据范围

Subtask n m 特殊性质
\(1(10pts)\) \(\leq10^3\) \(\le10^3\) \(\diagdown\)
\(2(20pts)\) \(\leq5\times10^4\) \(\leq5\times10^4\) 没有操作 2
\(3(10pts)\) \(\leq5\times10^4\) \(\leq5\times10^4\) 操作 2 中 \(l=r\)
\(4(20pts)\) \(\leq5\times10^4\) \(\leq5\times10^4\) \(\diagdown\)
\(5(20pts)\) \(\leq2\times10^5\) \(\leq2\times10^5\) 操作 2 中 \(l=r\)
\(6(20pts)\) \(\leq2\times10^5\) \(\leq2\times10^5\) \(\diagdown\)

对于 \(100\%\) 的数据,\(n,m\le2\times10^5,k\le10^9\)

思路

一共有三个操作,别看他描述的花里胡哨的,其实就是区间加,区间删除,区间查询。
但是,由于区间加的操作,它的叶节点存储的不止有一个数字,而是一个数列,所以他和普通的线段树还不同,它的节点还要再套点其他东西
那么,我们要套什么呢?
它不是还要查询最大值吗,那我们就套一个堆
又有问题了,有区间操作,怎么下传懒标?将堆与孩子的堆进行合并——左偏树,想到这里,恭喜你,掉坑里了
左偏树合并是将整个堆都合并过去,他又不只有一个孩子,它和一个孩子合并了,那其他孩子呢?所以左偏树不行
既然传懒标很麻烦,那我们就不穿懒标了呗,这里介绍一个小优化——标记永久化
什么意思呢,就是标记不下放,永远留在当前位置
我们用堆来记录懒标,但是本题中标记永久化的不彻底,还有一种情况需要下传懒标,当堆顶元素与要删除的元素相等时,我们要下放懒标记

代码

定义线段树:

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;
}

标签:02,maxx,qr,cur,int,mid,P7476,苦涩,ql
来源: https://www.cnblogs.com/yifan0305/p/16560644.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有