题意:

城墙上有n个连成一排的区域,每个区域中有一些弓箭手。弓箭手们都有r的防御半径,也就是说,弓箭手能够防守到向左或向右r个区域加上自己所处区域的范围。每个区域的防御等级为能够防守到该区域的弓箭手数量的总和,而城墙的防御等级为各区域防御等级的最小值。现在我们共有k名备用弓箭手可以增援这n个区域。问增援后城墙的防御等级的最大值能达到多少。

思路:

二分答案。

从左到右遍历这些区域,只要有区域i防御等级没有达到,就立即在相应的位置上布置弓箭手。自然是尽可能地布置在范围的最右边,这样就可以惠及更多的区域。

这里可以用到一个差分数组来标记。

代码:

typedef long long ll;
ll a[500010];
ll sum[500010];
ll b[500010];//防御等级
ll c[500010];//差分数组
ll n,r,k;
bool judge(ll x)
{
    ll tot=k;
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++)
    {
        c[i]+=c[i-1];
        ll now=b[i]+c[i];
        if(now<x)
        {
            ll need=x-now;
            if(tot<need)return false;
            tot-=need;
            c[i]+=need;
            c[min(n+1,i+2*r+1)]-=need;
            //因为前面是前缀和,从这里开始就脱离加的k的范围了,所以要减去need
        }
    }
    return true;
}
int main()
{
    scanf("%lld%lld%lld",&n,&r,&k);
    sum[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    for(ll i=1;i<=n;i++)
    {
        ll lb=max(1ll,i-r);
        ll rb=min(n,i+r);
        b[i]=sum[rb]-sum[lb-1];
    }
    ll low=0,high=2e18;
    ll ans=-1;
    while(low<=high)
    {
        ll mid=low+(high-low)/2;
        if(judge(mid))
        {
            ans=mid;
            low=mid+1;
        }
        else high=mid-1;
    }
    printf("%lld\n",ans);
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

bzoj1853 容斥原理+dfs Previous
高斯消元 Next