Codeforces 460C Present(二分+线段树)

点击这里进入题目


题目大意:给你n个数,可以做出m次修改,每次修改一个长度为w的区间,给这区间每一个数字加一,问修改后数组中最小值的最大值。


思路:看到区间修改和十的五次方的数据范围,很难想不到线段树,再看到最小值的最大值,也很难不想到二分,那么就直接二分最后的答案,然后用线段树维护check即可。


AC程序:

//库省略
using namespace std;
const int maxn=100005;
const ll inf=1e13;
int w,n,m;
ll a[maxn];
ll tree[4*maxn],lazy[4*maxn];
void built(int k,int l,int r)
{
    lazy[k]=0;
    if(l>=r)
    {
        tree[k]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    built(k*2,l,mid);
    built(k*2+1,mid+1,r);
    tree[k]=min(tree[k*2],tree[k*2+1]);
}
void pushdown(int now)
{
    if(lazy[now]==0)
    return;
    int left=now<<1,right=now<<1|1;
    tree[left]+=lazy[now];
    tree[right]+=lazy[now];
    lazy[left]+=lazy[now];
    lazy[right]+=lazy[now];
    lazy[now]=0;
}
ll ask(int tar,int now,int l,int r)
{
    if(l==r)
    {
        return tree[now];
    }
    int mid=(l+r)>>1;
    pushdown(now);
    if(tar<=mid)
    return ask(tar,now<<1,l,mid);
    else
    return ask(tar,now<<1|1,mid+1,r);
}
void change(ll val,int tl,int tr,int now,int l,int r)
{
    if(tl<=l && r<=tr)
    {
        tree[now]+=val;
        lazy[now]+=val;
        return;
    }
    //cout<<"FINE11"<<endl;
    pushdown(now);
    //cout<<"FINE22"<<endl;
    int mid=(l+r)>>1;
    int left=now<<1,right=now<<1|1;
    if(tl<=mid)
    change(val,tl,tr,left,l,mid);
    if(tr>mid)
    change(val,tl,tr,right,mid+1,r);
    tree[now]=min(tree[left],tree[right]);
}
bool check(ll num)
{
    ll mm=m;
    for(int i=1;i<=n;i++)
    {
        //cout<<"FINE1"<<endl;
        ll now=ask(i,1,1,n);
        if(now<num)
        {
            if(num-now>mm)
            return 0;
            else
            {
                //cout<<"FINE2"<<endl;
                change(num-now,i,min(i+w-1,n),1,1,n);
                mm-=num-now;
                //cout<<"FINE3"<<endl;
            }
        }
    }
    return 1;
}
int main()
{
    cin>>n>>m>>w;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    ll l=0,r=inf,mid;
    while(l<r)
    {
        built(1,1,n);
        //cout<<"YES"<<endl;
        mid=r-(r-l)/2;
        if(check(mid))
        {
            l=mid;
        }
        else
        {
            r=mid-1;
        }
        //cout<<"NO"<<endl;
    }
    cout<<l<<endl;
    return 0;
}