【洛谷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 }