【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 longconst 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"); //如果发现所有订单都能满足,输出0else printf("-1\n%d\n", r + 1); //否则第一行输出-1,第二行输出第一个不能满足的订单编号return 0;
}

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

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

相关文章

基于机器学习的网络入侵检测二元分类模型构建与性能评估(NSL-KDD数据集)

简介 该项目是一个基于NSL-KDD数据集的网络入侵检测系统&#xff0c;主要采用机器学习方法对网络流量数据进行使用了多种机器学习模型&#xff0c;如逻辑回归、线性SVM、多项式核SVM、高斯核SVM、决策树、随机森林、朴素贝叶斯和K近邻算法训练二元分类&#xff08;正常/异常&a…

bug总结(1)--变量取错

a c t i v i t y [ ′ t a g n a m e ′ ] 应为 activity[tag_name]应为 activity[′tagn​ame′]应为couponActivitList[0][‘name’] .隐藏的bug&#xff0c;在测试中竟然测不出来&#xff0c;而且上线了好久。为啥会出现这种低级错误呢&#xff1f;第一是写的时候不够仔细认…

Day26:安全开发-PHP应用模版引用Smarty渲染MVC模型数据联动RCE安全

目录 新闻列表 自写模版引用 Smarty模版引用 代码RCE安全测试 思维导图 PHP知识点&#xff1a; 功能&#xff1a;新闻列表&#xff0c;会员中心&#xff0c;资源下载&#xff0c;留言版&#xff0c;后台模块&#xff0c;模版引用&#xff0c;框架开发等 技术&#xff1a;输…

Selenium库快速查找网页元素及执行浏览器模拟操作

Selenium 是一个自动化测试工具&#xff0c;主要用于模拟用户在网页上的行为&#xff0c;进行自动化测试。它支持多种浏览器&#xff0c;并且可以在多种操作系统上运行。以下是 Selenium 库的一些主要特点和用途&#xff1a; 网页自动化测试&#xff1a; Selenium 可以模拟用户…

2024 年中国高校大数据挑战赛赛题 C:用户对博物馆评论的情感分析完整思路以及源代码分享

博物馆是公共文化服务体系的重要组成部分。国家文物局发布&#xff0c; 2021 年我国新增备案博物馆 395 家&#xff0c;备案博物馆总数达 6183 家&#xff0c;排 名全球前列&#xff1b;5605 家博物馆实现免费开放&#xff0c;占比达 90%以上&#xff1b;全国 博物馆举办展览 3…

【深度学习笔记】6_5 RNN的pytorch实现

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 6.5 循环神经网络的简洁实现 本节将使用PyTorch来更简洁地实现基于循环神经网络的语言模型。首先&#xff0c;我们读取周杰伦专辑歌词…

Python操作Redis 各种数据类型

本文将深入探讨如何使用Python操作Redis&#xff0c;覆盖从基础数据类型到高级功能的广泛主题。无论是字符串、列表、散列、集合还是有序集合&#xff0c;我们将一一解析&#xff0c;同时提供丰富的代码示例帮助读者更好地理解和应用。除此之外&#xff0c;本文还将介绍Redis的…

JVM和JVM内存管理

Java虚拟机&#xff08;Java Virtual Machine 简称JVM&#xff09;是运行所有Java程序的抽象计算机&#xff0c;是Java语言的运行环境&#xff0c;它是Java 最具吸引力的特性之一。Java源代码经过编译器编译后生成与平台无关的字节码文件&#xff08;.class文件&#xff09;。当…

【20240309】WORD宏设置批量修改全部表格格式

WORD宏设置批量修改全部表格格式 引言1. 设置表格文字样式2. 设置表格边框样式3. 设置所有表格边框样式为075pt4. 删除行参考 引言 这两周已经彻底变为office工程师了&#xff0c;更准确一点应该是Word工程师&#xff0c;一篇文档动不动就成百上千页&#xff0c;表格图片也是上…

计算机视觉(CV)自然语言处理(NLP)大模型应用,如何实现小模型

在人工智能领域&#xff0c;大模型已经成为引领创新和进步的重要推动力。它们不仅在自然语言处理、计算机视觉等任务中展现了强大的性能&#xff0c;还为各行各业带来了前所未有的机遇和挑战。本文将从一个高级写作专家的角度&#xff0c;深入探讨大模型的现状、技术突破以及未…

STM32之串口中断接收UART_Start_Receive_IT

网上搜索了好多&#xff0c;都是说主函数增加UART_Receive_IT()函数来着&#xff0c;实际正确的是UART_Start_Receive_IT()函数。 —————————————————— 参考时间&#xff1a;2024年3月9日 Cube版本&#xff1a;STM32CubeMX 6.8.1版本 参考芯片&#xff1a…

Svg Flow Editor 原生svg流程图编辑器(二)

系列文章 Svg Flow Editor 原生svg流程图编辑器&#xff08;一&#xff09; 说明 这项目也是我第一次写TS代码哈&#xff0c;现在还被绕在类型中头昏脑胀&#xff0c;更新可能会慢点&#xff0c;大家见谅~ 目前实现的功能&#xff1a;1. 元件的创建、移动、形变&#xff1b;2…

完全背包问题(一般写法与空间优化写法)

题目 有 N 种物品和一个容量是 V 的背包&#xff0c;每种物品都有无限件可用。 第 i 种物品的体积是 vi&#xff0c;价值是 wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。 输入格式 第一行两个整…

最多几个直角三角形python

最多几个直角三角形 问题描述思路代码实现 问题描述 最多可以组成几个直角三角形&#xff0c;一个边只能用一次。 输入描述&#xff1a; 第一行输入一个正整数T&#xff08;1<&#xff1d;T<&#xff1d;100&#xff09;&#xff0c;表示有T组测试数据. 对于每组测试数据…

贪心算法: 奶牛做题

5289. 奶牛做题 - AcWing题库 贝茜正在参加一场奶牛智力竞赛。 赛事方给每位选手发放 n 张试卷。 每张试卷包含 k 道题目&#xff0c;编号 1∼k。 已知&#xff0c;不同卷子上的相同编号题目的难度相同&#xff0c;解题时间也相同。 其中&#xff0c;解决第 i 道题&#xff08;…

【C语言】字符指针

在指针的类型中我们知道有一种指针类型为字符指针char* 一般使用&#xff1a; int main() { char ch w; char *pc &ch; *pc w; return 0; } 还有一种使用方式&#xff0c;如下&#xff1a; int main() { const char* pstr "hello bit.";//这⾥是把⼀个字…

plantUML使用指南之序列图

文章目录 前言一、序列图1.1 语法规则1.1.1 参与者1.1.2 生命线1.1.3 消息1.1.4 自动编号1.1.5 注释1.1.6 其它1.1.7 例子 1.2 如何画好 参考 前言 在软件开发、系统设计和架构文档编写过程中&#xff0c;图形化建模工具扮演着重要的角色。而 PlantUML 作为一种强大且简洁的开…

【stm32 外部中断】

中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行 中断优先级&#xff1a;当有多个中…

LoadBalancer (本地负载均衡)

1.loadbalancer本地负载均衡客户端 VS Nginx服务端负载均衡区别 Nginx是服务器负载均衡&#xff0c;客户端所有请求都会交给nginx&#xff0c;然后由nginx实现转发请求&#xff0c;即负载均衡是由服务端实现的。 loadbalancer本地负载均衡&#xff0c;在调用微服务接口时候&a…

考研复习C语言初阶(4)+标记和BFS展开的扫雷游戏

目录 1. 一维数组的创建和初始化。 1.1 数组的创建 1.2 数组的初始化 1.3 一维数组的使用 1.4 一维数组在内存中的存储 2. 二维数组的创建和初始化 2.1 二维数组的创建 2.2 二维数组的初始化 2.3 二维数组的使用 2.4 二维数组在内存中的存储 3. 数组越界 4. 冒泡…