[51nod1457]小K vs. 竹子

  小K的花园种着n颗竹子(竹子是一种茎部中空并且长得又高又快的热带植物)。此时,花园中第i颗竹子的高度是hi米,并且在每天结束的时候它生长ai米。

  实际上,小K十分讨厌这些竹子。他曾经试图去砍光它们,但由于竹子的茎部太坚固而失败了,然而,小K制作了魔法锤使这些竹子只能在地面上生长。

  由于魔法力量有限,他每天最多能使用K次魔法锤。每次他使用魔法锤敲击竹子,竹子的高度就会减少p米。通过这次改变,如果竹子的高度变为负数,那它转而会变为0米(但它不会消失)。换言之,如果一颗被魔法锤敲击的竹子的高度是h,那它的新高度将会是max(0, h - p)米。我们可以在一天中多次敲击同一颗竹子。

  小K将会从今天开始和这些竹子抗战m天。他的目标是在m天后使其中最高的竹子的高度最小化(即,“小K敲击竹子,然后竹子生长”的m次迭代)。找出m天后,最高的竹子的高度的最小可能值。

 Input
  输入的第一行包含四个以空格隔开的整数n,m,k和p(1 ≤ n ≤ 10^5 ,1 ≤ m ≤ 5000, 1 ≤k≤ 10, 1 ≤ p ≤ 10^9)。它们分别表示小K 花园中的竹子数,小K抗战的持续天数,每天小K能敲击竹子的最多次数和魔法锤的力量。
  接下来n行描述了竹子的特性,第i行(1 ≤ i ≤ n) 包含两个以空格隔开的整数hi和ai,(0 ≤ hi ≤ 10^9, 1 ≤ ai ≤ 10^9),分别表示第i颗竹子的初始高度和生长速率。
 Output
  输出m天后,最高的竹子的高度的最小可能值。

 

 

  跑去看了cf官网的题解。。

  先二分,判定是否可行的时候可以时光倒流。假设最后所有竹子的高度都是二分的答案mid,倒着过去每天,竹子就会变矮p米,不能让竹子的高度变成负数,魔法锤变成给竹子加高度。最后只要使所有竹子高度>=实际初始高度就好了。

  每次用魔法锤的时候,如果存在竹子再过一些天高度就变负数的话,就用在变负天数最少的竹子上。不然就用在最后和实际初始高度差最大的竹子上。用个堆维护一下。

  复杂度两个log

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cmath>
 7 #include<cstdlib>
 8 #define ll long long
 9 #define ull unsigned long long
10 #define ui unsigned int
11 #define d double
12 #define ld long double
13 const int maxn=100233;
14 
15 struct zs{int id;ll h,tim;};std::priority_queue<zs>q;
16 struct zsa{ll h,v;}a[maxn];
17 int i,j,k,n,m,kk,p;
18 
19 int ra,fh;char rx;
20 inline int read(){
21     rx=getchar(),ra=0,fh=1;
22     while(rx<'0'&&rx!='-')rx=getchar();
23     if(rx=='-')fh=-1,rx=getchar();
24     while(rx>='0')ra=ra*10+rx-48,rx=getchar();return ra*fh;
25 }
26 
27 bool operator <(zs a,zs b){return a.tim>b.tim;}
28 inline bool check(ll X){
29     register int i;int tim;zs now;
30     while(!q.empty())
31         q.pop();
32     //printf("mid:%lld\n",X);
33     for(i=1;i<=n;i++)if(X<a[i].v*m+a[i].h)
34         q.push((zs){i,X,X/a[i].v+1});//,printf("   %d\n",i);
35     for(i=1;i<=kk*m&&!q.empty();i++){
36         tim=(i+kk-1)/kk,
37         now=q.top(),q.pop();
38         if(now.tim<=tim)return 0;
39         now.h+=p,now.tim=now.h/a[now.id].v+1;
40         if(now.h<a[now.id].v*m+a[now.id].h)q.push(now);
41     }
42     return q.empty();
43 }
44 inline ll max(ll a,ll b){return a>b?a:b;}
45 int main(){
46     n=read(),m=read(),kk=read(),p=read();
47     ll l=1,r=0,mid;
48     for(i=1;i<=n;i++)a[i].h=read(),a[i].v=read(),r=max(r,a[i].h+m*a[i].v);
49     
50     while(l<r){//printf("   %lld %lld\n",l,r);
51         if(check(mid=l+r>>1))r=mid;else l=mid+1;
52     }
53     printf("%lld\n",l);
54 }
View Code

 

posted @ 2016-10-11 21:50  czllgzmzl  阅读(254)  评论(0编辑  收藏  举报