Codeforces Round #262 (Div. 2)C(二分答案,延迟标记)

这是最大化最小值的一类问题,这类问题通常用二分法枚举答案就行了。

二分答案时,先确定答案肯定在哪个区间内。然后二分判断,关键在于怎么判断每次枚举的这个答案行不行。

我是用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);
}

 

posted @ 2014-08-27 19:55  周洋  阅读(276)  评论(0编辑  收藏  举报