1. 程式人生 > >洛谷P1083 借教室 二分 + 差分

洛谷P1083 借教室 二分 + 差分

style 重新 pan sum std 但是 print false 一次

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

洛谷P1083 借教室 二分 + 差分