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 }