洛谷P1083 借教室 二分 + 差分

洛谷P1083 借教室

二分 + 差分(或说前缀和,其实前缀和更准确一点)

首先二分答案,即取 mid 个人,且他们不会冲突
然后O(n) 判断是否冲突
如何判断呢,首先我们发现 一个人的操作相当于是将 一些连续的山削去了一个高度
然后我们可以记录这座山被消了多少高度,但这样一次就要 O(N) 总共(n^2)
但是我们发现高度差只有两个地方变了,一个是起始,一个是终止
t[ i ] 表示 h[ i ] - h[ i-1 ]
改变过后 于是 t[ s ]-=d,t[ t+1 ]+=d ;
然后这样修改只要改两个地方
查询就这样一直加上去
话说树状数组 的 区间修改 单点修改也用到了这种思想 区间修改只改两个点

然后就可以O(n) 判断是否符合条件了
最后输出l+1 如果 l==m 表示 全部都可以选 输出 0

时间复杂度 O(nlogn)

话说还有一种方法
因为每次 差分数组又要重新计算 ,然而其实不用上次算mid 这次算 x
若mid < x 则直接加上这一部分的差分
如果 x<mid 就对这一段的 逆运算
这样

如果不算最后还是要 O(n) 遍历一下看下是否冲突 高度小于 0
这一部分计算其实是均摊 O(n) 的 , 这样就做到了 O(logn + n)的做法
但因为还有 O(n) 所以只优化了 300ms

 

 

 

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cmath>
 4 #include <cstring>
 5 #include <string>
 6 #include <algorithm>
 7 #include <iostream>
 8 #include <iomanip>
 9 #define ll long long 
10 using namespace std ; 
11 
12 const int maxn = 1000011  ;
13 const ll inf = 1e15 ;  
14 ll n,m,l,r,mid ;                                         // 
15 ll d[maxn],a[maxn],sum[maxn] ; 
16 int s[maxn],t[maxn] ; 
17 
18 inline ll read() 
19 {
20     char ch = getchar() ; 
21     ll x = 0, f = 1 ; 
22     while(ch<'0'||ch>'9') { if(ch=='-') ch = getchar() ; ch = getchar() ; } 
23     while(ch>='0'&&ch<='9') { x = x*10+ch-48 ; ch = getchar() ; } 
24     return x*f ;  
25 }
26 
27 inline bool check( int mid ) 
28 {
29     for(int i=1;i<=n;i++) sum[ i ] = 0 ; 
30     for(int i=1;i<=mid;i++) 
31         sum[ s[ i ] ]-=d[ i ] ,sum[ t[ i ]+1 ]+=d[ i ] ; 
32     int x = 0 ; 
33     for(int i=1;i<=n;i++) 
34     {
35         x+=sum[ i ] ; 
36         if( a[ i ] + x < 0  ) return false ; 
37     }
38     return true ; 
39 }
40 
41 int main()
42 {
43     n = read() ; m = read() ; 
44     for(int i=1;i<=n;i++) a[ i ] = read() ; 
45     for(int i=1;i<=m;i++) 
46         d[ i ] = read(),s[ i ] = read(),t[ i ] = read() ;  
47     l = 0 ; 
48     r = m ; 
49     while( l < r ) 
50     {
51         mid = (l+r+1)>>1 ; 
52         if(check(mid)) 
53             l = mid ; 
54         else 
55             r = mid-1 ; 
56     } 
57     if(l!=m) 
58         printf("-1\n%lld\n",l+1) ; 
59     else 
60         printf("0\n") ;     
61     return 0 ; 
62 }

 

posted @ 2017-06-18 23:26  third2333  阅读(142)  评论(0编辑  收藏  举报