借教室
Posted qswx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了借教室相关的知识,希望对你有一定的参考价值。
--srzer
借教室(Borrow Classrooms)
【Problem description】
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
【Input format】
第一行包含两个正整数n,m,表示天数和订单的数量。
第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。
接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。
【Output format】
如果所有订单均可满足,则输出只有一行,包含一个整数0。否则(订单无法完全满足)输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。
【Algorithm design】
线段树标记永久化/分治
【Problem analysis】
线段树标记永久化AC100
对于一个数组中的一连串数字进行修改,还要快速的看出有没有小于0的节点,自然想到最小区间线段树。维护一个线段树,根是叶子的最小值,根节点小于0就输出答案,这道题就被解决了。
虽然是O(log2n m),数据量最大(10^9),在加上快读优化后TLE95
在看了许多线段树题解后终于发现了这个神奇的优化:线段树标记永久化!
线段树中的构建,删除,实在是很难优化了,所以突破点在懒惰标记上,每次懒惰标记都下传占了一些时间。如果不更改树节点,直接在标记上永久修改,那么在下层节点修改之后,上层节点只要:s(sub) = min ( s(lkid) - add(lkid) , s(rkid) - add(rkid) )就可以将状态传递到上层;在上层节点被修改之后,下层节点无需变动,对于下层的变动传递到上层时,自然会被加上标记值
这个优化省了不少时间,但我觉得只能适用于无查找操作的线段树问题,因为只有根节点是真实值。
【Source code】
线段树标记永久化AC100
#include <bits/stdc++.h>
using namespace std;
struct node
{
int l,r,s,add;
#define l(i) seg_tree[i].l
#define r(i) seg_tree[i].r
#define s(i) seg_tree[i].s
#define add(i) seg_tree[i].add
#define lkid sub<<1
#define rkid sub<<1|1
}seg_tree[4000010];
int n,m,a[1000010];
int fastget()
{
int res=0;
char ch=getchar();
while(ch>‘9‘||ch<‘0‘)
ch=getchar();
while(ch<=‘9‘&&ch>=‘0‘)
{
res=(res<<1)+(res<<3)+ch-‘0‘;
ch=getchar();
}
return res;
}//快读函数
void build_tree(int sub,int l,int r)
{
l(sub)=l,r(sub)=r,add(sub)=0;
if(l==r)
{
s(sub)=a[l];
return ;
}
int mid=(l+r)>>1;
build_tree(lkid,l,mid);
build_tree(rkid,mid+1,r);
s(sub)=min(s(lkid),s(rkid));
return ;
}//建最小区间线段树
void cut_tree(int sub,int l,int r,int d)
{
if(l(sub)>r||r(sub)<l)
return ;
if(l(sub)>=l&&r(sub)<=r)
{
add(sub)+=d;
return ;
}
cut_tree(lkid,l,r,d);
cut_tree(rkid,l,r,d);
s(sub)=min(s(lkid)-add(lkid),s(rkid)-add(rkid));//标记永久化
return ;
}
int main()
{
freopen("classroom.in","r",stdin);
freopen("classroom.out","w",stdout);
n=fastget(),m=fastget();
for(int i=1;i<=n;i++)
a[i]=fastget();
build_tree(1,1,n);
for(int i=1;i<=m;i++)
{
int d,b,e;
d=fastget();
b=fastget();
e=fastget();
cut_tree(1,b,e,d);
if(s(1)-add(1)<0)//在永久化后,根节点也要算上标记
{
printf("-1 %d ",i);
return 0;
}
}
printf("0 ");
return 0;
}
以上是关于借教室的主要内容,如果未能解决你的问题,请参考以下文章