Mr. Kitayuta vs. Bamboosios
題目連接:http://codeforces.com/problemset/problem/505/E算法
參考:http://blog.csdn.net/qpswwww/article/details/46316647spa
貪心,二分.net
從數據規模上看,算法複雜度只能爲O(n)或者O(nlgn),彷佛不能直接求值,考慮二分MAX將求值問題轉化爲斷定性問題。然而考慮到砍伐後竹子高度變爲0的特殊狀況,考慮倒着作,即初始時每一個竹子高度均爲MAXi,天天晚上每一個竹子會減小高度a[i],天天白天能夠選擇對其中不超過K個竹子選擇將其的高度增長P,而且保證任什麼時候刻任何竹子高度均大於等於0,m天后是否可讓每一個竹子高度均大於等於h[i]。code
證實倒着推的正確性:只有在m天後每一個竹子高度均大於等於h[i],才能讓全部的竹子按照原來正向的順序,以和倒着作相同的操做反過來讓每一個竹子最終的高度小於等於MAXi,見下圖,綠色線表明倒着推的話一根竹子的天天生長狀況,藍色線表明正着推的話一根竹子的天天生長狀況(來自Codeforces官方題解) blog
能夠發現若是天天對這個竹子的操做相同的話,要想讓最後的這個竹子的高度和倒着推的高度同樣,以前天天正着推的高度都必須小於等於倒着推的高度。get
首先計算從MAXi開始,最多經歷多少天自由生長(減小高度a[i]),竹子的高度變爲負數,而後用一個小頂堆維護竹子變爲負數的天數(時間越小,越快變爲負數),天天取k個竹子進行砍伐(拔高處理)。若存在某一天來不及拔高竹子(存在某個竹子的高度在這一天以前就變爲0了),就能夠馬上斷定m天后不能可讓每一個竹子高度均大於等於h[i]。數學
如今咱們讓全部竹子倒過來生長,最終每一個竹子高度均大於等於0後,就須要讓每一個竹子的高度拔高,使得它們最終高度大於等於h[i],顯然此時因爲在生長過程當中,每一個竹子在任意時刻高度均大於等於0,故此時補充的操做不管是在什麼時候發生都是同樣的。這時直接經過數學方法,計算每一個竹子和h[i]之間相差的高度以考慮須要補上多少次操做就夠了。it
這道題是看着題解跪着作完的Orz,感受對二分的理解更深了一些,二分處理的都是斷定性問題,不能直接求值。io
代碼以下:
1 #include<cstdio> 2 #include<queue> 3 #include<iostream> 4 #define N 100000 5 #define LL long long 6 #define mid ((l+r)>>1) 7 using namespace std; 8 const LL MAX=1e15; 9 LL n,m,k,p,l,r; 10 LL h[N+5],a[N+5]; 11 LL now[N+5]; 12 typedef pair<LL,LL> P; 13 struct cmp{ 14 bool operator()(P a,P b){ 15 return a.first>b.first;//小頂堆 16 } 17 }; 18 bool judge(LL x){ 19 priority_queue<P,vector<P>,cmp> q; 20 for(LL i=0;i<n;++i){ 21 now[i]=x;//剛開始竹子的高度爲MAXi 22 if(x-m*a[i]>=0)continue;//不須要補充高度 23 q.push(make_pair(x/a[i],i));//P(到零的天數,下標) 24 } 25 LL times=0;//砍伐次數 26 for(;times<=k*m;times++){ 27 if(q.empty())break; 28 P temp=q.top(); 29 q.pop(); 30 if(temp.first<=times/k)return 0;//來不及補充高度,竹子在k天前就長成負數 31 LL index=temp.second; 32 now[index]+=p; 33 if(now[index]-m*a[index]>=0)continue;//不須要補充高度 34 q.push(make_pair(now[index]/a[index],index)); 35 } 36 if(times>k*m)return 0; 37 for(int i=0;i<n;++i){ 38 LL temp=m*a[i]+h[i]-now[i]; 39 if(temp<=0)continue; 40 else times+=((temp/p)+(LL)(temp%p!=0));//到h[i]還須要多少次補充高度 41 if(times>k*m)return 0; 42 } 43 return 1; 44 } 45 int main(void){ 46 scanf("%I64d%I64d%I64d%I64d",&n,&m,&k,&p); 47 l=1,r=MAX; 48 for(LL i=0;i<n;++i) 49 scanf("%I64d%I64d",&h[i],&a[i]); 50 while(l<r){//二分 51 if(judge(mid))r=mid; 52 else l=mid+1; 53 } 54 printf("%I64d\n",l); 55 }