NOIP2012 D2 T2 借教室 线段树 OR 二分法
题目描述:
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来n天的借教室信息,其中第i天学校有r(i)个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为d(j),s(j),t(j),表示某租借者需要从第s(j)天到第t(j)天租借教室(包括第s(j)天和第t(j)天),每天需要租借d(j)个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供d(j)个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申 请人修改订单。这里的无法满足指从第s(j)天到第t(j)天中有至少一天剩余的教室数量不足d(j)个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
输入格式
第一行包含两个正整数n,m,表示天数和订单的数量。
第二行包含n个正整数,其中第i个数为r(i),表示第i天可用于租借的教室数量。
接下来有m行,每行包含三个正整数d(j),s(j),t(j),表示租借的数量,租借开始、结束分别在第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从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 ≤ r(i),d(j) ≤ 10^9,1 ≤ s(j) ≤ t(j) ≤ n。
思路:
1.
线段树 维护区间的最小值,每次修改可以运用打标记的方法节省时间(关于节省时间 我后面有话说)。。。
2.
二分?(可惜并不会写,不知道怎么二分)。
晚上补了个二分的程序。
(若要看正解,请直接翻至页面底部。。 中间废话多)
只能写线段树了。。/(ㄒoㄒ)/~~
首先在TYVJ上写了一个不加lazy标记的线段树
//By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int tree[1000005],xx,yy,zz,lazy[1000005],n,m;
void build(int l,int r,int pos){
if(l==r){scanf("%d",&tree[pos]);return;}
int mid=(l+r)/2;
build(l,mid,pos*2);build(mid+1,r,pos*2+1);
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
int update(int l,int r,int pos){
if(l==r)return tree[pos]-=zz;
int mid=(l+r)/2;
if(mid<xx)return update(mid+1,r,pos*2+1);
else if(mid>=yy)return update(l,mid,pos*2);
else return tree[pos]=min(update(l,mid,pos*2),update(mid+1,r,pos*2+1));
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
int main(){
scanf("%d%d",&n,&m);
build(1,n,1);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&zz,&xx,&yy);
if(update(1,n,1)<0){printf("-1\n%d",i);return 0;}
}
puts("0");
return 0;
}
后果很惨烈。。。
改了改 改了很久很久(期间经过无数次崩溃),改成了加Lazy标记的线段树。
//By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int tree[10000005],xx,yy,zz,lazy[10000005],n,m;
void build(int l,int r,int pos){
if(l==r){scanf("%d",&tree[pos]);return;}
int mid=(l+r)/2;
build(l,mid,pos*2);build(mid+1,r,pos*2+1);
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
void push_down(int pos){
tree[pos]-=lazy[pos];
lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
lazy[pos]=0;
}
void update(int l,int r,int pos){
if(lazy[pos])push_down(pos);
if(l>yy||r<xx)return;
if(l>=xx&&r<=yy){lazy[pos]+=zz;push_down(pos);return;}
if(lazy[pos])push_down(pos);
int mid=(l+r)/2;
update(l,mid,pos*2);update(mid+1,r,pos*2+1);
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
int main(){
scanf("%d%d",&n,&m);
build(1,n,1);
for(int ii=1;ii<=m;ii++){
scanf("%d%d%d",&zz,&xx,&yy);update(1,n,1);
if(tree[1]<0){
printf("-1\n%d",ii);return 0;}
}
puts("0");
return 0;
}
还是T了一个点。。
怎么办呢 怎么办呢(此时我的内心接近崩溃。。)
队长过来了,,说:“你加个读入优化吧 balabala”
//By SiriusRen
#include <cstdio>
using namespace std;
int min(int x,int y){return x<y?x:y;}
int tree[10000005],xx,yy,zz,lazy[10000005],n,m;
void build(int l,int r,int pos){
if(l==r){scanf("%d",&tree[pos]);return;}
int mid=(l+r)/2;
build(l,mid,pos*2);build(mid+1,r,pos*2+1);
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
void push_down(int pos){
tree[pos]-=lazy[pos];
lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
lazy[pos]=0;
}
void update(int l,int r,int pos){
if(lazy[pos])push_down(pos);
if(l>yy||r<xx)return;
if(l>=xx&&r<=yy){lazy[pos]+=zz;push_down(pos);return;}
if(lazy[pos])push_down(pos);
int mid=(l+r)/2;
update(l,mid,pos*2);update(mid+1,r,pos*2+1);
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
int get(){
int x=0;char p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();
return x;
}
int main(){
scanf("%d%d",&n,&m);
build(1,n,1);
for(int ii=1;ii<=m;ii++){
zz=get();xx=get();yy=get();update(1,n,1);
if(tree[1]<0){
printf("-1\n%d",ii);return 0;}
}
puts("0");
return 0;
}
我就加了一个。 成功水过TYVJ的数据。【鼓掌!】
队长又过来了,说:“Vijos数据很强,你要不试试。。”
当时Vijos已挂,晚上回家就试了试。
果断挂了。【桑心】
各种卡评测机常数。。。。。。
//By SiriusRen
#include <cstdio>
using namespace std;
inline int min(int x,int y){return x<y?x:y;}
int tree[10000005],xx,yy,zz,lazy[10000005],n,m;
void build(int l,int r,int pos){
if(l==r){scanf("%d",&tree[pos]);return;}
int mid=(l+r)/2;
build(l,mid,pos*2);build(mid+1,r,pos*2+1);
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
void update(int l,int r,int pos){
if(lazy[pos]){
tree[pos]-=lazy[pos];
lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
lazy[pos]=0;
}
if(l>yy||r<xx)return;
if(l>=xx&&r<=yy){lazy[pos]+=zz; tree[pos]-=lazy[pos];
lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
lazy[pos]=0;return;}
if(lazy[pos]){
tree[pos]-=lazy[pos];
lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
lazy[pos]=0;
}
int mid=(l+r)/2;
update(l,mid,pos*2);update(mid+1,r,pos*2+1);
tree[pos]=min(tree[pos*2],tree[pos*2+1]);
}
inline int get(){
int x=0;char p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();
return x;
}
int main(){
register int n=0;register char p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')n=n*10+p-'0',p=getchar();
register int m=0;p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')m=m*10+p-'0',p=getchar();
build(1,n,1);
for(int ii=1;ii<=m;ii++){
xx=yy=zz=0;
p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')zz=zz*10+p-'0',p=getchar();
p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')xx=xx*10+p-'0',p=getchar();
p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')yy=yy*10+p-'0',p=getchar();
update(1,n,1);
if(tree[1]<0){printf("-1\n%d",ii);return 0;}
}
puts("0");
}
把get函数手动内置了一下。。。
哈哈AC了
网上找了个别的题库交了交。
这里:http://syzoj.com/problem/14(数据真心良心)
就光荣的挂了…….挂了……挂了。。。!!!
然后呢,我就各种改内置,一通乱搞。
//By SiriusRen
#include <cstdio>
using namespace std;
int tree[10000005],xx,yy,zz,lazy[10000005];
inline void build(int l,int r,int pos){
if(l==r){
register char p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')tree[pos]=tree[pos]*10+p-'0',p=getchar();
return;
}
register int mid=(l+r)>>1,lson=pos<<1,rson=pos+pos+1;
build(l,mid,lson);
build(mid+1,r,rson);
tree[pos]=tree[lson]<tree[rson]?tree[lson]:tree[rson];
}
inline void update(int l,int r,int pos){
register int lson=pos<<1,rson=pos+pos+1;
if(lazy[pos]){
tree[pos]-=lazy[pos];
lazy[lson]+=lazy[pos];
lazy[rson]+=lazy[pos];
lazy[pos]=0;
}
if(l>yy||r<xx)return;
if(l>=xx&&r<=yy){
lazy[pos]+=zz;
tree[pos]-=lazy[pos];
lazy[lson]+=lazy[pos];
lazy[rson]+=lazy[pos];
lazy[pos]=0;
return;
}
if(lazy[pos]){
tree[pos]-=lazy[pos];
lazy[lson]+=lazy[pos];
lazy[rson]+=lazy[pos];
lazy[pos]=0;
}
register int mid=(l+r)>>1;
update(l,mid,lson);
update(mid+1,r,rson);
tree[pos]=tree[lson]<tree[rson]?tree[lson]:tree[rson];
}
int main(){
register int n=0,m=0;
register char p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')n=n*10+p-'0',p=getchar();
p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')m=m*10+p-'0',p=getchar();
build(1,n,1);
for(int ii=1;ii<=m;ii++){
xx=yy=zz=0;
p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')zz=zz*10+p-'0',p=getchar();
p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')xx=xx*10+p-'0',p=getchar();
p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')yy=yy*10+p-'0',p=getchar();
update(1,n,1);
if(tree[1]<0){printf("-1\n%d",ii);return 0;}
}
puts("0");
}
卡时卡过去了哈哈哈哈
总结了几个小技巧:
1.
读入优化(粘个函数):
int get(){
int x=0;char p=getchar();
while(p<'0'||p>'9')p=getchar();
while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();
return x;
}
2.
少调用函数,节省函数调用的时间。必要时可以手动内置(Code Length什么的在AC面前都是浮云),手写min函数、max函数什么的,可以快很多。
3.
关于乘除法
多用位移操作,二进制的与、或什么的。(例: %8 就可以写成 &7,/2就可以写成>>1 之类的吧。。)
4.
inline和register ? 这种东西不知道靠不靠谱。。
5.
避免多次操作。
比如线段树中要多次用到pos*2、pos*2+1这种东西。怎么办呢? 我们可以开个变量保存它。用的时候就没有必要再重复计算了。
6.
最重要的一点吧。。 知道正解最好。。。。。。。。
附正解的二分算法:
二分前多少个订单可以满足,然后判断。
建立一个数组是a[i]=第i天需要的教室数-第i-1天需要的教室数
这样第i天需要的教室数就是a[]的前缀和。
每个订单a~b增加c就是a[a]+=c,a[b+1]-=c,把订单信息确定后之后扫一遍进行判断。
// by SiriusRen
#include <cstdio>
#include <cstring>
#define N 1000005
using namespace std;
int l=1,r,n,answer,mid,m,a[N],b[N],d[N],s[N],t[N];
bool solve(){
int ans=0;memset(b,0,sizeof(b));
for(int i=1;i<=mid;i++)b[s[i]]+=d[i],b[t[i]+1]-=d[i];
for(int i=1;i<=n;i++){
ans+=b[i];
if(a[i]<ans)return false;
}
return true;
}
int main(){
scanf("%d%d",&n,&m);r=m;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=r;i++)scanf("%d%d%d",&d[i],&s[i],&t[i]);
while(l<=r){
mid=(l+r)/2;
if(solve())answer=mid,l=mid+1;
else r=mid-1;
}
if(answer!=m)printf("-1\n%d\n",answer+1);
else puts("0");
}
写完线段树还是觉得二分so easy……
NOIP2012 D2 T2 借教室 线段树 OR 二分法的更多相关文章
- NOIP2012 D2 T2借教室
先上题目 题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息 ...
- NOIP2012借教室[线段树|离线 差分 二分答案]
题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自 ...
- NOIP2012 DAY2 T2借教室
题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然 ...
- noip 借教室 线段树95分做法
大致的思路是用线段树维护每个区间内部的最小值 段更新最小值 每次查某个区间的最小值是否满足租借要求 满足就借出去 update最小值 注意pushdown操作 还有一个从子区间提取答案的操作 提交地 ...
- 借教室 线段树and二分
描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望 ...
- 洛谷 P1083 [ NOIP 2012 ] 借教室 —— 线段树 / 二分差分数组
题目:https://www.luogu.org/problemnew/show/P1083 当初不会线段树的时候做这道题...对差分什么不太熟练,一直没A,放在那儿不管... 现在去看,线段树就直接 ...
- [vijos1782]借教室<线段树>
题目链接:https://vijos.org/p/1782 题意:一个区间1,n.m次操作,每次操作让l,r区间值减去d,当有任何一个值小于0就输出当前是第几个操作 这道题其实是没有什么难度的,是 ...
- CH Round #52 还教室[线段树 方差]
还教室 CH Round #52 - Thinking Bear #1 (NOIP模拟赛) [引子]还记得 NOIP 2012 提高组 Day2 中的借教室吗?时光飞逝,光阴荏苒,两年过去了,曾经借教 ...
- noip 2012 Day2 T2 借教室
一.暴力简述 甩链接.jpeg 首先我们不难看出,这道题————并不是一道多难的题,因为显然,第一眼看题目时便很容易地想到暴力如何打:枚举每一种订单,然后针对每一种订单,对区间内的每一天进行修改(做减 ...
随机推荐
- 两款工作流JBPM和CCBPM的对比
以国外流行的工作流jbpm4的模式与当今中国开源的ccbpm(ccflow和jflow的总称)流程引擎对照.以便让各位能够了解到中国国情的工作流引擎与国际流行的设计规则的差别.不同.与优缺点. 国外工 ...
- [实战经验][SQL Sever 2008 (R)解决方法累积
SQL Sever 2008 (R)的安装图解及配置 http://www.soft6.com/v9/2009/jcsj_1030/115821.html 产品密钥,选择“输入产品密钥”,输入:PTT ...
- 【sqli-labs】 less9 GET - Blind - Time based. - Single Quotes (基于时间的GET单引号盲注)
加and http://localhost/sqli/Less-9/?id=1' and '1'='1%23 http://localhost/sqli/Less-9/?id=1' and '1'=' ...
- EF CodeFirst 基础命令
PM> enable-migrations 已在项目"EasyWeChat.Data"中启用迁移.若要覆盖现有迁移配置,请使用 -Force 参数. PM> add-m ...
- (转)Arcgis for Js之GeometryService实现测量距离和面积
http://blog.csdn.net/gisshixisheng/article/details/40540601 距离和面积的测量时GIS常见的功能,在本节,讲述的是通过GeometryServ ...
- java操作Excel的poi的字体设置
package com.java.poi; import org.apache.poi.hssf.usermodel.HSSFWorkbook;import org.apache.poi.ss.use ...
- Spring Cloud Alibaba、Spring Boot、Spring Cloud对应版本关系
Spring Boot Spring Cloud Spring Cloud Alibaba 2.1.x Greenwich 0.9.x 2.0.x Finchley 0.2.x 1.5.x Edgwa ...
- 【剑指Offer】42、和为S的两个数字
题目描述: 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 输出描述: 对应每个测试案例,输出两个数, ...
- 【JavaScript框架封装】自己动手封装一个涵盖JQuery基本功能的框架及核心源码分享(单文件版本)
整个封装过程及阅读JQuery源码的过程基本上持续了一个月吧,最终实现了一个大概30%的JQuery功能的框架版本,但是里面涉及的知识点也是非常多的,总共的代码加上相关的注释大概在3000行左右吧,但 ...
- [jzoj5791]【NOIP2008模拟】阶乘 (数学)
传送门 Description 有n个正整数a[i],设它们乘积为p,你可以给p乘上一个正整数q,使p*q刚好为正整数m的阶乘,求m的最小值. Input 共两行. 第一行一个正整数n. 第二行n个正 ...