这是最大化最小值的一类问题,这类问题通常用二分法枚举答案就行了。
二分答案时,先确定答案肯定在哪个区间内。然后二分判断,关键在于怎么判断每次枚举的这个答案行不行。
我是用a[i]数组表示初始时花的高度,b[i]表示要达到当前枚举的答案(即mid的值)需要这朵花再涨多少。这两个数组很好算,关键是一次浇连续的w朵花,如何更新区间(暴力的O(n2)的去更新就超时了)?可以用线段树,但是这道题没有涉及区间查询,就是在一个数组上更新区间,用线段树未免小题大做。那么其实这种更新就用延迟标记的思想(懒操作)就可以O(n)的解决了。具体方法参见这道很裸的题:hdu4970
lazy[]这个标记数组的作用其实就是能记录更新区间到哪里停止.
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<map> #include<set> #include<vector> #include<algorithm> #include<stack> #include<queue> using namespace std; #define INF 1000000000 #define eps 1e-8 #define pii pair<int,int> #define LL long long int const int maxn=110009; int n,m,w,a[maxn],b[maxn],lazy[maxn],ans=0; /*lazy[]这个标记数组的作用其实就是能记录更新区间到哪里停止*/ int main() { //freopen("in6.txt","r",stdin); scanf("%d%d%d",&n,&m,&w); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } int l=1,r=maxn+INF,mid; while(l<=r) { memset(lazy,0,sizeof(lazy)); mid=(r+l)/2; for(int i=1; i<=n; i++) b[i]=max(0,mid-a[i]); int tm=m,x=0; for(int i=1;i<=n;i++)//开始浇水 { x+=lazy[i];//要先看这一步是不是有标记 b[i]-=x;//把前面对影响到当前b[i]的浇水操作浇上 if(b[i]>0)//还大于0,那么就必须要从它开始再来一段浇水了 { tm-=b[i]; if(tm<0) break;//天数限制到了 lazy[i+w]-=b[i]; x+=b[i]; } } if(tm<0) r=mid-1; else { ans=mid; l=mid+1; } } printf("%d\n",ans); }
时间: 2025-01-18 17:47:23