CodeForces 460C Present

题意

给定一个长度为 \(n\) 的序列 \(a\),有 \(m\) 次操作,每次可以将 \(a\) 中的连续 \(w\) 个元素增加 \(1\),最大化最终序列的最小值。

\(\texttt{Data Range:}1\leq w\leq n\leq 10^5,1\leq m\leq 10^5\)

题解

咋一到数据结构题就缩卵呢,于是就理所当然的被神 \(\texttt{x}\)\(\texttt{gzc}\) 教育了 >_<

db9tRU.jpg

看到这种最小值最大这东西考虑二分答案,于是只需要对于二分出的答案 \(x\) 判定 \(m\) 次操作之后最小值是否不小于 \(x\)

考虑贪心搞。如果 \(a\) 中存在一个值小于 \(x\) 我们就把他加到大于等于 \(x\) 即可,但是为了不浪费操作次数,我们可以直接加到 \(x\)。这里我的实现是以这个数为左端点来考虑。

但是有一些位置不可能成为长度为 \(w\) 的区间的左端点。这个时候考虑完前面所有的操作之后直接把 \([n-w+1,n]\) 这个区间加上剩下的操作次数即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51; 
ll n,m,w,l,r,mid,res;
ll x[MAXN],diff[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline ll check(ll mid)
{
    ll c=0,cur=m,p;
    for(register int i=1;i<=n;i++)
    {
        diff[i]=x[i]-x[i-1];
    }
    for(register int i=1;i<=n-w+1;i++)
    {
        c+=diff[i];
        if(cur>0&&c<mid)
        {
            p=min(cur,mid-c),diff[i]+=p,diff[i+w]-=p,cur-=p,c+=p;
        }
    }
    diff[n-w+1]+=cur,diff[n+1]-=cur,c=0;
    for(register int i=1;i<=n;i++)
    {
        c+=diff[i];
        if(c<mid)
        {
            return 0;
        }
    }
    return 1;
}
int main()
{
    n=read(),m=read(),w=read(),l=0x3f3f3f3f,r=0x3f3f3f3f;
    for(register int i=1;i<=n;i++)
    {
        x[i]=read(),l=min(l,x[i]);
    }
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        check(mid)?l=mid+1,res=mid:r=mid-1;
    }
    printf("%d\n",res);
}
posted @ 2020-08-30 09:16  Karry5307  阅读(212)  评论(1编辑  收藏  举报