【AcWing】蓝桥杯集训每日一题Day1|二分|差分|503.借教室(C++)

503. 借教室

503. 借教室 - AcWing题库
难度:简单
时/空限制:1s / 128MB
总通过数:8052
总尝试数:26311
来源:NOIP2012提高组
算法标签二分差分

题目内容

在大学期间,经常需要租借教室。
大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。
教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。 
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来 n n n天的借教室信息,其中第 i i i天学校有 r i r_{i} ri个教室可供租借。
共有 m m m份订单,每份订单用三个正整数描述,分别为 d j , s j , t j d_{j},s_{j},t_{j} dj,sj,tj表示某租借者需要从第 s j s_{j} sj天到第 t s t_{s} ts天租借教室(包括第 s j s_{j} sj天和第 t j t_{j} tj天),每天需要租借 d j d_{j} dj个教室。 
我们假定,租借者对教室的大小、地点没有要求。
即对于每份订单,我们只需要每天提供 d j d_{j} dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。
如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。
这里的无法满足指从第 s j s_{j} sj天到第 t j t_{j} tj天中有至少一天剩余的教室数量不足 d j d_{j} dj个。 
现在我们需要知道,是否会有订单无法完全满足。
如果有,需要通知哪一个申请人修改订单。

输入格式

第一行包含两个正整数 n , m n,m n,m,表示天数和订单的数量。 
第二行包含 n n n个正整数,其中第 i i i个数为 r i r_{i} ri,表示第 i i i天可用于租借的教室数量。 
接下来有 m m m行,每行包含三个正整数 d j , s j , t j d_{j},s_{j},t_{j} dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。 
每行相邻的两个数之间均用一个空格隔开。
天数与订单均用从 11 开始的整数编号。

输出格式

如果所有订单均可满足,则输出只有一行,包含一个整数0。
否则(订单无法完全满足)输出两行,第一行输出一个负整数 −1,第二行输出需要修改订单的申请人编号。

数据范围

1 ≤ n , m ≤ 1 0 6 1≤n,m≤10^6 1n,m106
0 ≤ r i , d j ≤ 1 0 9 0≤r_{i},d_{j}≤10^9 0ri,dj109
1 ≤ s j ≤ t j ≤ n 1≤s_{j}≤t_{j}≤n 1sjtjn

输入样例:
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出样例:
-1
2
题目解析

数据范围在 1 0 6 10^6 106,时间复杂度要控制在 O ( n log ⁡ n ) O(n\log n) O(nlogn)/ O ( n ) O(n) O(n)
如何通过数据范围判断时间复杂度可以参考这篇文章
蓝桥杯暴力求解与高频考点-CSDN博客
如果直接模拟,不优化的话
![[Pasted image 20240309164127.png]]

可以从前往后依次遍历每个订单,可以开个数组,统计一下每一天一共需要多少教室
s … t s \dots t st天,每天需要 d d d个教室,可以枚举for循环一下,从s~t枚举一遍,cnt[i]+=d,每次加完之后,判断这一天需要教室数量是不是小于给定教室数量,如果是的话,表示满足;不是的话,表示不满足。
一共需要处理 1 0 6 10^6 106个订单,每个订单的区间长度最大 O ( n ) = 1 0 6 O(n)=10^6 O(n)=106,时间复杂度就是 1 0 6 ∗ 1 0 6 = 1 0 12 10^6*10^6=10^{12} 106106=1012,肯定会TLE(Time Limit Exceeded时间超限)。
如果代码不想超时,需要将时间复杂度控制在 1 0 8 10^8 108以内(1s是 1 0 8 10^8 108)

优化的话
如果按照题意进行动态地做,动态地给每一个区间加上同一个d,然后再去动态地判断这个区间内的某一个值是不是大于给定的值。

可以用线段树来做,可是线段树时间复杂度比较高,可能会TLE,线段树的题目n一般会给到 1 0 5 10^5 105。要使用比线段树常数更小的算法

1. 差分

如果把这个题变成一个判定性的问题,
判断一下能不能处理前k个订单,这个时候,这个题目就变成了给k个区间,每个区间加一个相同的数,加完之后需要计算一下每个数最终的值是多少
![[Pasted image 20240309164206.png]]

经典的模板题,相当于算法里的公式
797.差分
有一个标准做法,可以做到线性的时间复杂度 O ( n + m ) O(n+m) O(n+m)
判定性问题可以用差分来做

差分的原理

比如每一天可以用的教室数量是 r 1 … r n r_{1}\dots r_{n} r1rn,用数组r表示
要统计一下每天需要多少教室 a 1 … a n a_{1}\dots a_{n} a1an,用a数组来表示
初始的时候都是0,
给一个s~t的订单,给 a s … a t a_{s}\dots a_{t} asat之间的每个数全部加上一个d
先将a数组进行一个变形
a 0 = 0 a_{0}=0 a0=0
b 1 = a 1 − a 0 b_{1}=a_{1}-a_{0} b1=a1a0 b 2 = a 2 − a 1 b_{2}=a_{2}-a_{1} b2=a2a1以此类推,一直到 b n = a n − a n − 1 b_{n}=a_{n}-a_{n-1} bn=anan1
相当于对a数组进行一个差分变换
发现
如果给我们一个差分数组b,可以把原数组a反推回来
给原数组a,可以把差分数组b求出来
差分数组和原数组的信息量是一模一样的
差分数组推原数组
a 1 = b 1 + a 0 = b 1 a_{1}=b_{1}+a_{0}=b_{1} a1=b1+a0=b1
a 2 = b 2 + a 1 = b 1 + b 2 a_{2}=b_{2}+a_{1}=b_{1}+b_{2} a2=b2+a1=b1+b2
a n = b n + a n − 1 = b 1 + ⋯ + b n a_{n}=b_{n}+a_{n-1}=b_{1}+\dots+b_{n} an=bn+an1=b1++bn
原数组就是差分数组的前缀和数组

转换完之后
如果想对s~t,每个数加上一个d的话
如果要对原数组操作,要去修改 O ( n ) O(n) O(n)个数
但是这个操作对差分数组的影响就很少了
b 1 = a 1 − a 2 b 2 = a 2 − a 1 … b s − 1 = a s − 1 − a s − 2 b s = a s − a s − 1 b s + 1 = a s + 1 − a s … b t = a t − a t − 1 b t + 1 = a t + 1 − a t b t + 2 = a t + 2 − a t + 1 … \begin{array}{} b_{1}=a_{1}-a_{2} \\ b_{2}=a_{2}-a_{1} \\ \dots \\ b_{s-1}=a_{s-1}-a_{s-2} \\ b_{s}=a_{s}-a_{s-1} \\ b_{s+1}=a_{s+1}-a_{s} \\ \dots \\ b_{t}=a_{t}-a_{t-1} \\ b_{t+1}=a_{t+1}-a_{t} \\ b_{t+2}=a_{t+2}-a_{t+1} \\ \dots \end{array} b1=a1a2b2=a2a1bs1=as1as2bs=asas1bs+1=as+1asbt=atat1bt+1=at+1atbt+2=at+2at+1
这个操作只会对 a s … a t a_{s}\dots a_{t} asat将每个数全部加上d
因此
b 1 … b s − 1 b_{1}\dots b_{s-1} b1bs1都是不变的,因为 a 0 … a s − 1 a_{0}\dots a_{s-1} a0as1不变,所以差值也不变
b s b_{s} bs的话, a s a_{s} as加上一个d, a s − 1 a_{s-1} as1不变,所以 b s b_{s} bs加了一个d
b s + 1 … b t b_{s+1}\dots b_{t} bs+1bt,由于 a s … a t a_{s}\dots a_{t} asat每个数全部加了一个d,所以差值是不变的,所以b都是不变的
b t + 1 b_{t+1} bt+1的话, a t + 1 a_{t+1} at+1不变, a t a_{t} at加了一个d,所以差值会减一个d,所以 b t + 1 b_{t+1} bt+1减一个d
b t + 2 b_{t+2} bt+2开始到后面的,因为从 a t + 1 … a n a_{t+1}\dots a_{n} at+1an一直都不变,所以不变
不变 { b 1 = a 1 − a 2 b 2 = a 2 − a 1 … b s − 1 = a s − 1 − a s − 2 + d b s = a s − a s − 1 不变 { b s + 1 = a s + 1 − a s … b t = a t − a t − 1 − d b t + 1 = a t + 1 − a t 不变 { b t + 2 = a t + 2 − a t + 1 … \begin{array}{} \\ 不变\begin{cases} b_{1}=a_{1}-a_{2} \\ b_{2}=a_{2}-a_{1} \\ \dots \\ b_{s-1}=a_{s-1}-a_{s-2} \\ \end{cases}\\ +d\qquad b_{s}=a_{s}-a_{s-1} \\ 不变\begin{cases} b_{s+1}=a_{s+1}-a_{s} \\ \dots \\ b_{t}=a_{t}-a_{t-1} \\ \end{cases}\\ -d\qquad b_{t+1}=a_{t+1}-a_{t} \\ 不变\begin{cases} b_{t+2}=a_{t+2}-a_{t+1} \\ \dots \end{cases} \end{array} 不变 b1=a1a2b2=a2a1bs1=as1as2+dbs=asas1不变 bs+1=as+1asbt=atat1dbt+1=at+1at不变{bt+2=at+2at+1

对于原数组的操作,本来要操作 O ( n ) O(n) O(n)个数,对于差分数组的影响,只会影响 b s b_{s} bs b t + 1 b_{t+1} bt+1两个数,这样就可以将修改操作从 O ( n ) O(n) O(n)变成 O ( 1 ) O(1) O(1)
改变 O ( n ) O(n) O(n)个数,需要两层循环,现在只用改变两个数,就不用写循环了,可以把时间复杂度从 O ( n 2 ) O(n^2) O(n2)变成 O ( n ) O(n) O(n)
这样的话每个操作就不要对原数组操作,而是对差分数组进行操作,差分数组求完之后,再通过差分数组求出来原数组,相当于做一个变换

这样可以用 O ( n + m ) O(n+m) O(n+m)的时间复杂度,判断出前k个订单有没有出现矛盾

2. 二分

原问题
把所有的订单取出来,编号是1~m
先到先得,按照编号顺序依次处理每个订单

二段性

这些个能处理的订单,有一个二段性
比如说在这个问题里面,第k个订单是最后一个能处理的订单
从第k+1个订单开始就不能处理了
![[Pasted image 20240309185859.png]]

把前k个区间全部加到数组上,每一天需要的教室数量都是小于等于给定的教室数量,对于k前面的任何一个数x,同样能处理x个订单,显然是成立的
前x个订单严格比前k个订单少一些区间,相当于在某些数上少加了一些d,加上那些之后不会超过给定值,不加那些数同样不会超过给定值
最后一个能处理的订单是第k个订单的话,对于小于等于k等任何一个数x,前x个订单一定都是可以处理的

同理

第一个不能处理的订单是第k+1个订单的话,如果把前k+1个区间加到天数数组r上的话,一定会有某一天,它需要的教室数量大于给定值
对于大于等于k+1的任何一个x,1~x一定也都是不能处理的
![[Pasted image 20240309185935.png]]

因为前k+1个区间加完之后,在某一天已经大于给定值了,再加上一些额外的数,仍然会大于给定值


对于x来说,当x取1~k之间的数的时候,它是满足的
当x大于k的时候,是不满足的

具有二段性的话,就可以通过二分把这个边界二分出来,
789.数的范围

如何二分

比如最后一个能处理的订单是第k个订单,
左右边界是1~m
每次二分一个中点mid
如果1~mid,能满足的话,说明最后一个能满足的订单在mid或者在mid的右边,所以答案会落在 [ m i d , m ] [mid,m] [mid,m]这个区间,
![[Pasted image 20240309191741.png]]

就可以删掉左边的区间,把二分的左端点L设成mid
![[Pasted image 20240309191817.png]]

再二分中点
取一个mid,如果处理mid这个订单会出现矛盾,说明1~mid是无法处理的,说明最后一个能处理的订单在mid的严格左边
![[Pasted image 20240309190835.png]]

因此相当于把右边的区间删掉,把右端点R置成mid-1
![[Pasted image 20240309190918.png]]

每次将搜索范围缩小一半,缩小完之后保证答案一定在搜索范围内,最后当范围内只有一个数的时候,它就是答案

二分模板用哪个,取决于mid属于左半边区间还是右半边区间,取整的问题,二分的话,整数不一定能够刚好整除,奇数的话是上取整还是下取整,如果模板用错可能会有死循环

代码
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;
//算前缀和,10^6个10^9,有可能会爆int,需要用到long long

const int N = 1000010;

int n, m;
int w[N];
//用w存r,不用r,因为二分的时候会用到变量r,防止变量冲突
int d[N], s[N], t[N];
LL b[N];

//检查前mid订单能不能满足
bool check (int mid)
{
	memset(b, 0, sizeof b); //先把差分数组初始化为0,不能让上次检查的结果,影响下次检查的过程
	for (int i = 1; i <= mid; i++) //依次处理每一个订单
	{
		b[s[i]] += d[i];
		b[t[i] + 1] -= d[i];
	}

	LL s = 0;  //用一个前缀和来计算当前的每一a_i是多少
	for (int i = 1; i <= n; i++) //以此类推每一天
	{
		s += b[i]; //每次更新一下前缀和
		if (s > w[i]) return false;  //发现当前教室数量大于给定教室数量,return false
	}

	return true;  //否则return true
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int 1 = 1; i <= n; i++) scanf("%d", &w[i]);
	//依次读入每天可以用的教室数量
	for (int i = 1; i <= m; i++) scanf("%d%d%d", &d[i], &s[i], &t[i]);
	//依次读入m个订单d,s,t,订单数量和起始时间

	//二分
	int l = 0; r = m;   //0表示一个订单都不能满足
	while (l < r)
	{
		//求中点
		int mid = l + r + 1 >> 1; //因为l=mid,+1用上取整;如果r=mid,用下取整
		if (check(mid)) l = mid;  //如果发现[1,mid)可以满足,表示答案应该在[mid,m]
		else r = mid -1;
	}

	if (r == m) puts("0"); //如果发现所有订单都能满足,输出0
	else printf("-1\n%d\n", r + 1); //否则第一行输出-1,第二行输出第一个不能满足的订单编号

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux上安装torch-geometric(pyg)1.7.2踩坑记录

重点&#xff1a;1.一定要在创建虚拟环境的时候设置好python版本。2.一定要先确定使用1.X还是2.X的pyg库&#xff0c;二者不兼容。3.一定要将cuda、torch、pyg之间的版本对应好。所以&#xff0c;先确定pyg版本&#xff0c;再确定torch和cuda的版本。 结论&#xff1a;如果在u…

【解读】OWASP大语言模型应用程序十大风险

OWASP大型语言模型应用程序前十名项目旨在教育开发人员、设计师、架构师、经理和组织在部署和管理大型语言模型&#xff08;LLM&#xff09;时的潜在安全风险。该项目提供了LLM应用程序中常见的十大最关键漏洞的列表&#xff0c;强调了它们的潜在影响、易利用性和在现实应用程序…

基本计算器II

文章目录 题目解析算法解析算法模拟第一步 第二步第三步第四步第五步第六步最后一步 代码 题目解析 题目链接 我们先来看一下题目这个题目的意思很明确就是给你一个算数式让你计算结果并返回并且给了很多辅助条件来帮助你。 算法解析 那么我们来看看这个题目有哪些做法&…

Qt 定时器事件

文章目录 1 定时器事件1.1 界面布局1.2 关联信号槽1.3 重写timerEvent1.4 实现槽函数 启动定时器 2 定时器类 项目完整的源代码 QT中使用定时器&#xff0c;有两种方式&#xff1a; 定时器类&#xff1a;QTimer定时器事件&#xff1a;QEvent::Timer&#xff0c;对应的子类是QTi…

SQL中常见的DDL操作及示例,数据库操作及表操作

目录 一、数据库操作 1、创建数据库 2、查看所有数据库 3、使用数据库 4、删除数据库 二、表操作&#xff1a; 1、创建表 2、查看表结构 3、修改表结构 3.1 添加列 3.2 修改列数据类型 3.3 修改列名 3.4 删除列 3.5 修改表名 3.6 删除表 注意&#xff1a; 在数…

云计算项目十一:构建完整的日志分析平台

检查k8s集群环境&#xff0c;master主机操作&#xff0c;确定是ready 启动harbor [rootharbor ~]# cd /usr/local/harbor [rootharbor harbor]# /usr/local/bin/docker-compose up -d 检查head插件是否启动&#xff0c;如果没有&#xff0c;需要启动 [rootes-0001 ~]# system…

OpenCV学习笔记(四)——对视频的读取操作

目录 读取视频内容 将彩色视频转换为灰色视频 读取视频内容 读取视频文件通常分为读取文件、验证是否打开成功打开文件、逐帧读取视频文件、释放资源和关闭窗口 &#xff08;1&#xff09;读取文件 在OpenCV中&#xff0c;通常使用VedioCapture来读取视频流&#xff0c;Vedi…

linux多线程编程使用互斥量的原理分析和应用实例

目录 概述 1 保护对共享变量的访问&#xff1a;互斥量 1.1 认识互斥量 1.2 互斥锁API 1.2.1 互斥锁初始化函数 1.2.2 互斥锁函数 1.2.3 互斥锁变体函数 1.3 互斥锁使用方法 1.4 互斥锁死锁 2 互斥量的应用介绍 2.1 创建与销毁 2.1.1 创建互斥量 2.1.2 销毁互斥量 …

UnicodeDecodeError: ‘gbk‘和Error: Command ‘pip install ‘pycocotools>=2.0

今天重新弄YOLOv5的时候发现不能用了&#xff0c;刚开始给我报这个错误 subprocess.CalledProcessError: Command ‘pip install ‘pycocotools&#xff1e;2.0‘‘ returned non-zero exit statu 说这个包安装不了 根据他的指令pip install ‘pycocotools&#xff1e;2.0这个根…

FPGA的时钟资源

目录 简介 Clock Region详解 MRCC和SRCC的区别 BUFGs 时钟资源总结 简介 7系列FPGA的时钟结构图&#xff1a; Clock Region&#xff1a;时钟区域&#xff0c;下图中有6个时钟区域&#xff0c;用不同的颜色加以区分出来 Clock Backbone&#xff1a;从名字也能看出来&#x…

【数据结构】万字长文图解+代码实现AVL树

目录 一、概念 二、图解 1.图解插入 2.图解右单旋 3.图解左单旋 4.图解右左双旋 5.图解左右双旋 6.验证是否是AVL树 三、代码实现 一、概念 AVL树是一种高度平衡的二叉搜索树&#xff0c;得名于其发明者的名字&#xff08;G. M. Adelson-Velskii和E. M. Landis&#xff0…

ELK-介绍及Elasticsearch集群搭建

ELK是三个开源软件的缩写&#xff0c;分别为Elasticsearch、Logstash、kibana它们都是开源软件。后来新增了一个FileBeat&#xff0c;它是一个轻量及的日志收集处理工具&#xff0c;因为Logstash由java程序开发&#xff0c;比较消耗内存资源&#xff0c;后来将Logstash使用go语…

微信小程序如何实现下拉刷新

1.首先在你需要实现下拉刷新页面的json文件中写入"enablePullDownRefresh": true。 2.在js文件的onPullDownRefresh() 事件中实现下拉刷新。 实现代码 onPullDownRefresh() {console.log(开始下拉刷新)wx.showNavigationBarLoading()//在标题栏中显示加载图标this.d…

一个将图片转3D的开源项目TripoSR

TripoSR AI是StabilityAI联合发布的图生3D模型&#xff0c;TripoSR是一个快速的3D物体重建模型。TripoSR能够在不到一秒钟的时间内从单张图片生成高质量的3D模型。TripoSR模型的特点是能够快速处理输入&#xff0c;在 NVIDIA A100 GPU 上不到 0.5 秒的时间内生成高质量的 3D 模…

用于无人机路径规划的增强型粒子群优化

摘要&#xff1a; 近年来&#xff0c;元启发式算法被广泛用于解决多目标优化问题&#xff08;MOOPs&#xff09;。粒子群优化&#xff08;PSO&#xff09;由于其在解决MOOPs方面的有效性&#xff0c;在这些算法中得到了普及。然而&#xff0c;PSO的性能高度依赖于其探索和利用能…

解锁安卓开发利器:深度探析ADB【安卓开发】

引言 在安卓开发与维护过程中&#xff0c;我们经常会遇到一些限制&#xff0c;比如无法直接访问某些系统功能&#xff0c;或者在某些定制系统中 受到限制 。为了解决这些问题&#xff0c;我们需要一种有效的工具来管理和调试安卓设备&#xff0c;而这时候ADB&#xff08;Andro…

离线数仓(五)【数据仓库建模】

前言 今天开始正式数据仓库的内容了, 前面我们把生产数据 , 数据上传到 HDFS , Kafka 的通道都已经搭建完毕了, 数据也就正式进入数据仓库了, 解下来的数仓建模是重中之重 , 是将来吃饭的家伙 ! 以及 Hive SQL 必须熟练到像喝水一样 ! 第1章 数据仓库概述 1.1 数据仓库概念 数…

【打工日常】使用docker部署个人实时在线文档协助编辑器

一、Etherpad介绍 Etherpad是一个高度可定制的开源在线编辑器&#xff0c;提供真正实时的协作编辑。放在自己的服务器里面&#xff0c;可以更大程度的保护自己工作的隐私&#xff0c;并且Etherpad允许您实时协作编辑文档&#xff0c;就像在浏览器中运行的实时多人编辑器一样这样…

Linux网络基础2之http

这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 目录 一、认识URL 1.认识URL 2.urlencode和urldecode 二、HTTP协议格式 1.快速构建http请求和相应的报文格式 三、http demo 1.GET和POST 2.HTTP的状态码 3.http的特征 4.HTTP常见Head…

ARTS Week 20

Algorithm 本周的算法题为 1222. 可以攻击国王的皇后 在一个 下标从 0 开始 的 8 x 8 棋盘上&#xff0c;可能有多个黑皇后和一个白国王。 给你一个二维整数数组 queens&#xff0c;其中 queens[i] [xQueeni, yQueeni] 表示第 i 个黑皇后在棋盘上的位置。还给你一个长度为 2 的…