【离散化 线段树】P3740 [HAOI2014] 贴海报|普及+

news2025/5/18 12:56:25

本文涉及知识点

C++线段树

[HAOI2014] 贴海报

题目描述

Bytetown 城市要进行市长竞选,所有的选民可以畅所欲言地对竞选市长的候选人发表言论。为了统一管理,城市委员会为选民准备了一个张贴海报的 electoral 墙。

张贴规则如下:

  1. electoral 墙是一个长度为 N N N 个单位的长方形,每个单位记为一个格子;

  2. 所有张贴的海报的高度必须与 electoral 墙的高度一致的;

  3. 每张海报以 A B 表示,即从第 A A A 个格子到第 B B B 个格子张贴海报;

  4. 后贴的海报可以覆盖前面已贴的海报或部分海报。

现在请你判断,张贴完所有海报后,在 electoral 墙上还可以看见多少张海报。

输入格式

第一行,两个正整数 N , M N,M N,M,分别表示 electoral 墙的长度和海报个数。

接下来 M M M 行,每行两个正整数 A i , B i A_i,B_i Ai,Bi,表示每张海报张贴的位置。

输出格式

输出贴完所有海报后,在 electoral 墙上还可以看见的海报数。

样例 #1

样例输入 #1

100 5
1 4
2 6
8 10
3 4
7 10

样例输出 #1

4

提示

约束条件

10 ≤ N ≤ 10000000 , 1 ≤ M ≤ 1000 , 1 ≤ A i ≤ B i ≤ 10000000 10\le N \le 10000000,1\le M\le 1000,1\le A_i \le B_i \le 10000000 10N10000000,1M1000,1AiBi10000000

所有的数据都是正整数,数据之间有一个空格。

离散化+线段树

第i块海报,如果被第i+1到M块海报覆盖,则看不到。
由于M只有1000,故可以离散。
i = T to 1
线段树:
保存类型,int:被覆盖的点数
设置类型,int: 覆盖次数。
注意
离散化的时候,不但要离散L,R,还要离散L-1,R+1。否则[1,6] [1,3] [5,6]会变成[1,4][1,2][3,4]。前者可以看到3个海报,后者只能看到2个。

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) ;
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	scanf("%d", &n);
	vector<T> ret(n);
	for(int i=0;i < n ;i++) {
		cin >> ret[i];
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 12 * 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
	}
private:
	char  puffer[N], * m_p;
};

template<int N = 12 * 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {
		fread(buffer, 1, N, stdin);
	}
	inline int Read() {
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		return f ? -x : x;
	}
private:
	char buffer[N], * S = buffer;
};

template<class TSave, class TRecord >
class CRangUpdateLineTree
{
protected:
	virtual void OnQuery(const TSave& save, const int& iSaveLeft, const int& iSaveRight) = 0;
	virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) = 0;
	virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, const int& iSaveLeft, const int& iSaveRight) = 0;
	virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) = 0;
};

template<class TSave, class TRecord >
class CVectorRangeUpdateLineTree : public CRangUpdateLineTree<TSave, TRecord>
{
public:
	CVectorRangeUpdateLineTree(int iEleSize, TSave tDefault, TRecord tRecordNull) :m_iEleSize(iEleSize)
		, m_save(iEleSize * 4, tDefault), m_record(iEleSize * 4, tRecordNull) {
		m_recordNull = tRecordNull;
	}
	void Update(int iLeftIndex, int iRightIndex, TRecord value)
	{
		Update(1, 0, m_iEleSize - 1, iLeftIndex, iRightIndex, value);
	}
	void Query(int leftIndex, int rightIndex) {
		Query(1, 0, m_iEleSize - 1, leftIndex, rightIndex);
	}
	//void Init() {
	//	Init(1, 0, m_iEleSize - 1);
	//}
	TSave QueryAll() {
		return m_save[1];
	}
	void swap(CVectorRangeUpdateLineTree<TSave, TRecord>& other) {
		m_save.swap(other.m_save);
		m_record.swap(other.m_record);
		std::swap(m_recordNull, other.m_recordNull);
		assert(m_iEleSize == other.m_iEleSize);
	}
protected:
	//void Init(int iNodeNO, int iSaveLeft, int iSaveRight)
	//{
	//	if (iSaveLeft == iSaveRight) {
	//		this->OnInit(m_save[iNodeNO], iSaveLeft);
	//		return;
	//	}
	//	const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
	//	Init(iNodeNO * 2, iSaveLeft, mid);
	//	Init(iNodeNO * 2 + 1, mid + 1, iSaveRight);
	//	this->OnUpdateParent(m_save[iNodeNO], m_save[iNodeNO * 2], m_save[iNodeNO * 2 + 1], iSaveLeft, iSaveRight);
	//}
	void Query(int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) {
		if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) {
			this->OnQuery(m_save[iNodeNO], iSaveLeft, iSaveRight);
			return;
		}
		if (iSaveLeft == iSaveRight) {//没有子节点
			return;
		}
		Fresh(iNodeNO, iSaveLeft, iSaveRight);
		const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
		if (mid >= iQueryLeft) {
			Query(iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight);
		}
		if (mid + 1 <= iQueryRight) {
			Query(iNodeNO * 2 + 1, mid + 1, iSaveRight, iQueryLeft, iQueryRight);
		}
	}
	void Update(int iNode, int iSaveLeft, int iSaveRight, int iOpeLeft, int iOpeRight, TRecord value)
	{
		if ((iOpeLeft <= iSaveLeft) && (iOpeRight >= iSaveRight))
		{
			this->OnUpdate(m_save[iNode], iSaveLeft, iSaveRight, value);
			this->OnUpdateRecord(m_record[iNode], value);
			return;
		}
		if (iSaveLeft == iSaveRight) {
			return;//没有子节点
		}
		Fresh(iNode, iSaveLeft, iSaveRight);
		const int iMid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
		if (iMid >= iOpeLeft)
		{
			Update(iNode * 2, iSaveLeft, iMid, iOpeLeft, iOpeRight, value);
		}
		if (iMid + 1 <= iOpeRight)
		{
			Update(iNode * 2 + 1, iMid + 1, iSaveRight, iOpeLeft, iOpeRight, value);
		}
		// 如果有后代,至少两个后代
		this->OnUpdateParent(m_save[iNode], m_save[iNode * 2], m_save[iNode * 2 + 1], iSaveLeft, iSaveRight);
	}
	void Fresh(int iNode, int iDataLeft, int iDataRight)
	{
		if (m_recordNull == m_record[iNode])
		{
			return;
		}
		const int iMid = iDataLeft + (iDataRight - iDataLeft) / 2;
		Update(iNode * 2, iDataLeft, iMid, iDataLeft, iMid, m_record[iNode]);
		Update(iNode * 2 + 1, iMid + 1, iDataRight, iMid + 1, iDataRight, m_record[iNode]);
		m_record[iNode] = m_recordNull;
	}
	vector<TSave> m_save;
	vector<TRecord> m_record;
	TRecord m_recordNull;
	const int m_iEleSize;
};

template<class T = int>
class CDiscretize //离散化
{
public:
	CDiscretize(vector<T> nums)
	{
		sort(nums.begin(), nums.end());
		nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
		m_nums = nums;
		for (int i = 0; i < nums.size(); i++)
		{
			m_mValueToIndex[nums[i]] = i;
		}
	}
	int operator[](const T value)const
	{
		auto it = m_mValueToIndex.find(value);
		if (m_mValueToIndex.end() == it)
		{
			return -1;
		}
		return it->second;
	}
	int size()const
	{
		return m_mValueToIndex.size();
	}
	vector<T> m_nums;
protected:
	unordered_map<T, int> m_mValueToIndex;
};
//P3740 [HAOI2014] 贴海报

typedef int TSave;
typedef int  TRecord;
class  CMyLineTree : public  CVectorRangeUpdateLineTree<TSave, TRecord>
{
public:
	TSave m_ans = 0;
	using CVectorRangeUpdateLineTree::CVectorRangeUpdateLineTree;
protected:
	virtual void OnQuery(const TSave& save, const int& iSaveLeft, const int& iSaveRight) {
		m_ans += save;
	}
	virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) override
	{
		if (0 == update) { return; }
		save = iSaveRight - iSaveLeft + 1;
	}
	virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, const int& iSaveLeft, const int& iSaveRight)  override
	{
		par = left + r;
	}
	virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) override
	{
		old += newRecord;
	}
};

class Solution {
		public:
			int Ans(vector<pair< int, int>>& LR) {
				vector<int> tmp;
				for (const auto& [L, R] : LR) {
					tmp.emplace_back(L - 1);
					tmp.emplace_back(L);
					tmp.emplace_back(R);
					tmp.emplace_back(R+1);
				}
				CDiscretize dis(tmp);
				const int N = dis.size();
				CMyLineTree lineTree(N, 0, 0);
				int ans = 0;
				for (int i = LR.size() - 1; i >= 0; i--) {
					const int left = dis[LR[i].first];
					const int r = dis[LR[i].second];
					lineTree.m_ans = 0;
					lineTree.Query(left, r);
					ans += lineTree.m_ans < (r - left + 1);
					lineTree.Update(left, r, 1);
				}
				return ans;
			}
		};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	int n,T;
	cin >> T >> n ;
	auto a = Read<pair<int,int>>(n);	
	auto res = Solution().Ans(a);
	cout << res;
#ifdef _DEBUG		
	/*printf("T=%d,", T);*/
	//Out(a, "a=");
	//Out(edge, "edge=");
#endif // DEBUG	
	return 0;
}

单元测试

	vector<pair< int, int>> a;
		TEST_METHOD(TestMethod11)
		{
			a = { {1,4},{2,6},{8,10},{3,4},{7,10}};
			auto res = Solution().Ans(a);
				AssertEx(4, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSP 2024 提高级第一轮(CSP-S 2024)单选题解析

单选题解析 第 1 题 在 Linux 系统中&#xff0c;如果你想显示当前工作目录的路径&#xff0c;应该使用哪个命令&#xff1f;&#xff08;A&#xff09; A. pwd B. cd C. ls D. echo 解析&#xff1a;Linux 系统中&#xff0c;pwd命令可以显示当前工作目录的路径。pwd&#x…

六、绘制图片

文章目录 1.创建一个红色图片2.加载bmp图片3.加载png、jpg图片 前面的几个示例&#xff0c;我们已经展示过如果在Linux系统下使用xlib接口向窗口中绘制文本、线、矩形&#xff1b;并设置文本、线条的颜色。并利用xlib提供的接口结合事件处理机制完成了一个自绘按钮控件功能。有…

Java 面向对象详解和JVM底层内存分析

先关注、点赞再看、人生灿烂&#xff01;&#xff01;&#xff01;&#xff08;谢谢&#xff09; 神速熟悉面向对象 表格结构和类结构 我们在现实生活中&#xff0c;思考问题、发现问题、处理问题&#xff0c;往往都会用“表格”作为工具。实际上&#xff0c;“表格思维”就是…

深度学习---知识蒸馏(Knowledge Distillation, KD)

一、知识蒸馏的本质与起源 定义&#xff1a; 知识蒸馏是一种模型压缩与迁移技术&#xff0c;通过将复杂高性能的教师模型&#xff08;Teacher Model&#xff09;所学的“知识”迁移到轻量级的学生模型&#xff08;Student Model&#xff09;&#xff0c;使学生模型在参数量和计…

基于CNN卷积神经网络的带频偏QPSK调制信号检测识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2024b 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff09…

【DAY21】 常见的降维算法

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 目录 PCA主成分分析 t-sne降维 线性判别分析 (Linear Discriminant Analysis, LDA) 作业&#xff1a; 什么时候用到降维 降维的主要应用场景 知识点回顾&#xff1a; PCA主成分分析t-sne降维LDA线性判别 通常情况下&#xff0c;…

校园社区小程序源码解析

基于ThinkPHP、FastAdmin和UniApp开发的校园社区小程序源码&#xff0c;旨在为校园内的学生和教职员工提供一个便捷的在线交流和服务平台。 该小程序前端采用UniApp进行开发&#xff0c;具有良好的跨平台兼容性&#xff0c;可以轻松发布到iOS和Android平台。同时&#xff0c;后…

第6章:文件权限

一、文件权限概述 Linux为了保证系统中每个文件的安全&#xff0c;引入了文件权限机制。针对于系统中的每一个文件Linux都可以提供精确的权限控制。它可以做到不同的用户对同一个文件具有不同的操作权利。而通常这个权利包括以下3个&#xff1a; 读的权利&#xff08;Read&…

C语言| 指针变量的定义

C语言| 指针的优点-CSDN博客 * 表示“指向”&#xff0c;为了说明指针变量和它所指向的变量之间的联系。 int * i&#xff1b;//表示指针变量i里面存放的地址&#xff0c;所指向的存储单元里的【数据】。 【指针变量的定义】 C语言规定所有变量&#xff0c;在使用前必须先定…

IntelliJ IDEA给Controller、Service、Mapper不同文件设置不同的文件头注释模板、Velocity模板引擎

通过在 IntelliJ IDEA 中的 “Includes” 部分添加多个文件头模板&#xff0c;并在 “Files” 模板中利用这些包含来实现不同类型文件的注释。以下是为 Controller、Service、Mapper 文件设置不同文件头的完整示例&#xff1a; 1. 设置 Includes 文件头模板 File > Settin…

【论文阅读】BEVFormer

〇、Introduction BEVFormer是现在端到端无人驾驶中常使用的一个Backbone&#xff0c;用于将六个视角下的图像转换为鸟瞰图视角下的特征&#xff0c;转换出的BEV特征则会被用于后续模块的特征交互。然而在这个模型设计的初期&#xff0c;其最本质的意图是为了提取用于各种CV任…

IDEA编辑器设置的导出导入

背景 最近新换了电脑&#xff0c;因为之前是 Intel 芯片的 Mac&#xff0c;这次换了 arm 架构的 M 芯片的 Mac&#xff0c;旧 Mac 上的很多软件不兼容&#xff0c;所以就没有选择换机数据迁移&#xff0c;一点一点下载、配置了所有环境。 导出 IDEA 支持设置的导入导出&…

成功案例丨从草图到鞍座:用先进的发泡成型仿真技术变革鞍座制造

案例简介 在鞍座制造中&#xff0c;聚氨酯泡沫成型工艺是关键环节&#xff0c;传统依赖实验测试的方法耗时且成本高昂。为解决这一问题&#xff0c;意大利自行车鞍座制造商 Selle Royal与Altair合作&#xff0c;采用Altair Inspire PolyFoam软件进行发泡成型仿真。 该工具帮助团…

电机试验平台:创新科技推动电动机研究发展

电机试验平台是电机制造和研发过程中不可或缺的重要设备&#xff0c;其功能涵盖了电机性能测试、电机寿命测试、电机质量评估等多个方面。随着科技的不断发展和电机应用领域的日益扩大&#xff0c;对电机试验平台的要求也越来越高。本文将从现代化电机试验平台的设计与应用两个…

【springcloud学习(dalston.sr1)】Zuul路由访问映射规则配置及使用(含源代码)(十二)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; springcloud学习&#xff08;dalston.sr1&#xff09;系统文章汇总如下&#xff1a; 【springcloud学习(dalston…

Qt与Hid设备通信

什么是HID&#xff1f; HID&#xff08;Human Interface Device&#xff09;是‌直接与人交互的电子设备‌&#xff0c;通过标准化协议实现用户与计算机或其他设备的通信&#xff0c;典型代表包括键盘、鼠标、游戏手柄等。‌ 为什么HID要与qt进行通信&#xff1f; 我这里的应…

2024 山东省ccpc省赛

目录 I&#xff08;签到&#xff09; 题目简述&#xff1a; 思路&#xff1a; 代码&#xff1a; A&#xff08;二分答案&#xff09; 题目简述&#xff1a; 思路&#xff1a; 代码&#xff1a; K&#xff08;构造&#xff09; 题目&#xff1a; 思路&#xff1a; 代…

SAP HCM 0008数据存储逻辑

0008信息类型&#xff1a;0008信息类型是存储员工基本薪酬的地方&#xff0c;因为很多企业都会都薪酬带宽&#xff0c;都会按岗定薪&#xff0c;所以在上线前为体现工资体系的标准化&#xff0c;都会在配置对应的薪酬关系&#xff0c;HCM叫间接评估&#xff0c;今天我们就分析下…

如何使用通义灵码辅助学习C++编程 - AI编程助手提升效率

一、引言 C 是一门功能强大且灵活的编程语言&#xff0c;在软件开发、系统编程、游戏开发等领域广泛应用。然而&#xff0c;其复杂的语法和丰富的特性使得学习曲线较为陡峭。对于初学者而言&#xff0c;在学习过程中难免会遇到各种问题&#xff0c;如语法理解困难、代码调试耗…

【Docker】CentOS 8.2 安装Docker教程

目录 1.卸载 2.安装依赖 3.设置yum源 4.安装Docker 5.启动Docker 6.设置Docker开机自启 7.验证Docker是否安装成功 8.配置多个国内镜像地址 9.重启Docker 10.Docker指令大全 10.1.启动与关闭Docker 10.2.Docker镜像操作 10.3.Docker容器操作 10.4.Docker Compose操作…