借教室

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;

}









以上是关于借教室的主要内容,如果未能解决你的问题,请参考以下文章

codevs1217 借教室

借教室

[前缀和+二分]借教室

借教室

洛谷P1083 借教室

CODEVS 1217 借教室