【洛谷P1083】[NOIP2012]借教室

借教室

【题目描述】

在n天中每天有一个可以借出的教室数,有m个订单,每个订单从第l天到第r天要借用x个教室。问能否满足所有的订单,若不能,输出第一个不能满足的订单编号。

 

思路:

1.1 ≤ n,m ≤ 10^6,区间的整体修改可以用差分数组实现,每次修改的时间复杂度为O(1),查询的时间复杂度为O(n)。

2.若从第一个订单到第m个订单向上枚举,每次都利用差分数组算一遍每天的订单数,时间为O(m*n);但从题目中“输出第一个不能满足的订单编号”可以得到启示:二分答案。

时间复杂度就成了O(nlogm)。

 

补充:

差分数组的原理:

  差分数组与部分和数组相类似

 

  部分和数组是用每个数据记录原数组中多个元素的和(前缀和 或 后缀和),利用两个数据的差求原数组中多个数据的和,以前缀和数组为例,                  sum[i]=data[1]+data[2]+data[3]+……+data[i]

  sum[i]-sum[j-1]=(data[1]+data[2]+data[3]+……+data[i])-(data[1]+data[2]+data[3]+……+data[j-1])

  =data[j]+data[j+1]+……+data[i]。

 

  差分数组每个数据记录原数组中该元素与其上一个元素的差,即diff[i]=data[i]-data[i-1],显然:

  data[i]=data[i-1]+diff[i]=data[i-2]+diff[i-1]+diff[i]=……

  =diff[1]+diff[2]+diff[3]+……+diff[i]

  我们可以这样描述:data数组是diff数组的前缀和数组

  一个显而易见的性质:当diff[i]改变时,data[i~n]的值会有相同的改变,如diff[i]+=1,diff[j+1]-=1,就相当于区间[i,j]都加了1,这样便可以用O(1)的时间实现区间修改了。

 

贴代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 int data[1000020],l[1000020],r[1000020],d[1000020],diff[1000020],n,m,f,g;
 7 bool isok(int x)
 8 {
 9     memset(diff,0,sizeof(diff));
10     for(int i=1;i<=x;i++)        //订单 1~x
11     {
12         diff[l[i]]+=d[i];        //差分数组 
13         diff[r[i]+1]-=d[i];         
14     }
15     int sum=0; 
16     for(int i=1;i<=n;i++)
17     {
18         sum+=diff[i];
19         if(sum>data[i]) return 0;
20     }
21     return 1;
22 }
23 int main()
24 {
25     scanf("%d%d",&n,&m);
26     for(int i=1;i<=n;i++)
27     scanf("%d",&data[i]);
28     for(int i=1;i<=m;i++)
29     scanf("%d%d%d",&d[i],&l[i],&r[i]);
30     if(isok(m))
31     {
32         printf("0");
33         return 0;
34     }
35     printf("-1\n");
36     f=1;g=m;
37     while(f<g)        //二分订单数 
38     {
39         int mid=(f+g)/2;
40         if(isok(mid)) f=mid+1;
41         else g=mid;
42     }
43     printf("%d\n",f);
44     return 0;
45 }

 

posted @ 2018-03-02 12:36  yjk  阅读(163)  评论(0编辑  收藏  举报