NKOJ-1887 借教室 【NOIP2012 day2】
来源:互联网 发布:0.32双丝并绕数据 编辑:程序博客网 时间:2024/05/24 06:43
P1887【NOIP2012 day2】借教室
时间限制 : 40000 MS 空间限制 :重点内容 128000 KB
问题描述
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。 面对海量租借教室的信息,我们自然希望编程解决这个问题。 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj, sj, tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。 我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。 借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。 现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
输入格式
第一行包含两个正整数n, m,表示天数和订单的数量。 第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。 接下来有m行,每行包含三个正整数dj, sj, tj,表示租借的数量,租借开始、结束分别在第几天。 每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。
输出格式
如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足) 输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。
样例输入
4 3 2 5 4 3 2 1 3 3 2 4 4 2 4
样例输出
-1 2
提示
输入输出样例说明】第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第2 个申请人修改订单。
【数据范围】
对于 10%的数据,有1 ≤ n, m ≤ 10;对于 30%的数据,有1 ≤ n, m ≤ 1000;对于 70%的数据,有1 ≤ n, m ≤ 10^5;对于 100%的数据,有1 ≤ n, m ≤ 10^6, 0 ≤ ri, dj≤ 10^9, 1 ≤ sj≤ tj≤ n。
情不自禁地感叹
有些代码,你没看懂的时候以为是写代码的人脑壳有Trouble
看懂之后,就开始怀疑自己脑壳有没有Trouble了
题解
首先我们说一种新的(对我来说是新的)建树方式
已知数的左边界l,右边界r
那么对于该线段树,它的编号就是(l+r)|(l!=r)
解释
这个建树方式非常巧妙因为我们知道,线段树的分段的区间是不停地二分的所以任意两段不同的线段,他们的左边界和右边界之和(l+r)一定不相同证明一下(此处线段长度>=4) 假设某线段边界(l+r) 那么它二分之后,子线段的边界之和 左线段=l+(l+r)/2 < l+r 右线段=(l+r)/2+1+r > l+r 同理易得,子线段的子线段同样满足上述规律但是有一种情况比较特殊,就是线段和线段的中点的(l+r)是有可能相等的那么此时,我们就需要一种区分方式这个时候,就用到了(l+r)|(l!=r) l==r时表示这是点 那么此时 l+r为偶数,(1!=r)==0 (l+r)|(l!=r)一定为偶数 l!=r时表示这是线段 那么此时 l+r 无论为奇数还是偶数,(l!=r)==1 (l+r)|(l!=r)一定为奇数此时不同的线段/点的标号也一定不一样(自己参考上面的方法证明)这样子,线段(点)的标号就可以使用 (l+r)|(l!=r)表示那么我们要开的数组大小仅为线段长度的2倍
然后就是十分简单的线段树操作了
我们定义emp(empty的缩写)为此时的空房间的数量,lazy(代码中的wait)为滞留的操作
如何定义线段的emp呢??
因为当这一段当中有一个点的emp<0时这段线段就有房间被订满了我们希望emp能够表示出没有房间的状态所以我们将线段的emp定位整个线段中最小的空房间数于是,当某一次修改(订购房间)的操作覆盖了[l,r]的区间时由于一整段线段的值都要减去订购的房间数(暂定为a)所以我们将[l,r]的lazy定为(a),表示减去的数量为a同时,由于我们定义了emp为线段中最小的空房间数所以emp-a<0时,表示空房间数最小的那一天的空房间已经被过度定购了(不满足题意的可满足解)我们就可以快速地判断出这一段区间是否能够满足要求
另外,当订购的区间只与[l,r]有交集时,我们就选择先把lazy当中的操作下达下去,再细分线段进行操作(详见代码)
同时要注意,及时地更新大区间的emp
注意
第一点
原本的建树方式即使使用的是相同的思路和解法,也会超时
因为无论是声明结构体,还是不停地通过计算子区间序号和调用子区间的边界,在巨大的数据范围下都是十分庞大的操作
而此处的建树方法能够只通过(l+r)|(l!=r)来计算区间的序号,大大减少了调用次数
同时这种方法也更节省空间
第二点
尽量使lazy数组的操作可叠加
因为可叠加的lazy数组可以一次处理多次下达的指令,时间复杂度和运算量会大大减少(主要是时间复杂度前面的那个常数大大减小了)
第三点(总结)
线段数最大的优势在于
我们在大区间即可确定修改某一个点的值是否合法
要尽量使用这个优点,高效地进行判断
lazy数组最大地优势在于累加操作
对于整段区间的操作都可以累加
直到某个操作只对交集部分操作时才往下进行操作
在写lazy时也要尽量使用这个优势,尽量累加次数,而非简单地推迟一次操作
最后附上代码
#include <iostream>#include <cstdio>#include <cstdlib>#define mid (l+r>>1)using namespace std;inline int input(){ char c=getchar();int o; while(c>57||c<48)c=getchar(); for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48; return o;}int ll,rr,ADD;int emp[2001234],lazy[2001234];void BT(int l,int r){ int ori=(l+r)|(l!=r); if(l<r) { int ls=(l+mid)|(l!=mid),rs=(mid+1+r)|(mid+1!=r); BT(l,mid); BT(mid+1,r); emp[ori]=min(emp[ls],emp[rs]);//更新大区间的emp } else emp[ori]=input();//所有的点都是从小到大依次被细分到的}bool add(int l,int r){ int ori=(l+r)|(l!=r); bool res=1; if(ll<=l&&r<=rr) if(emp[ori]<ADD)return 0;//在大区间判断修改的合法性 else { emp[ori]-=ADD; lazy[ori]+=ADD;//累加lazy操作 return 1; } //当操作涉及的区域只与[l,r]的子区间相关时 int ls=(l+mid)|(l!=mid),rs=(mid+1+r)|(mid+1!=r); if(lazy[ori]>0)//下达lazy操作 { emp[ls]-=lazy[ori]; lazy[ls]+=lazy[ori]; emp[rs]-=lazy[ori]; lazy[rs]+=lazy[ori]; lazy[ori]=0; } if(l!=r)//细分区间进行操作 { if(ll<=mid)res=(res&add(l,mid)); if(rr>mid)res=(res&add(mid+1,r)); } emp[ori]=min(emp[ls],emp[rs]);//更新emp的值 return res;}int main(){ int n=input(),m=input(); BT(1,n); for(int i=1;i<=m;i++) { ADD=input();ll=input();rr=input(); if(add(1,n)==0){printf("-1\n%d",i);return 0;} } printf("0"); return 0;}
阅读全文
0 0
- NKOJ-1887 借教室 【NOIP2012 day2】
- NOIP2012 Day2 T2 借教室
- NOIP2012提高组day2第2题 借教室
- NOIP2012 Day2 借教室-二分答案+差分
- Noip2012 Day2 T2 借教室 (二分答案+差分)
- noip2012 借教室
- NOIP2012 借教室
- NOIP2012 借教室
- NOIP2012 借教室
- NOIp2012 借教室
- 1266. [NOIP2012] 借教室
- NOIP2012 借教室(classroom)
- noip2012借教室
- NOIP2012 借教室【二分】
- NOIP2012 借教室
- 【NOIP2012】codevs1217 借教室
- [NOIP2012] 借教室
- 【NOIP2012】借教室
- angularjs input 表单校验指令整理
- 使用@ResponseBody的中文乱码问题
- Dom 倒计时按钮
- Leetcode197. Rising Temperature
- webpack安装与基本配置
- NKOJ-1887 借教室 【NOIP2012 day2】
- WeiXinUtil
- .NET中的反射(2)
- display、position填坑
- Dom 密码强度检查
- 动态获取下拉框的值与ajax中对方法的隐藏与显示
- 定义asyncExportFile服务,通过异步下载导出文件
- 终极方案:SwipeRefreshLayout刷新控件与webview控件冲突
- 线性与二次判别分析