洛谷3594 [POI2015]WIL-Wilcze doły(单调队列)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83108399

题目

给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0。请找到最长的一段连续区间,使得该区间内所有数字之和不超过p。

特性

选择一个区间[i,i+d-1],那么我们选择的最长区间一定在这个区间附近,也就是连续的。

题解

单调队列
很明显是一个O(N)的算法,那么时间只够我们枚举一个右端点。
枚举右端点后,最优的左端点在哪里呢?很明显,这个左端点随着右端点的右移,它也会随之右移或者原地不动。
我们选的数 = 区间和 - 区间一段长度为d的区间
我们要维护的就是区间内一段长度为d的最大和区间,需要维护一个单调递减的队列。
当选的数大于p时,就要缩短区间,同时弹出起点超出last的长度为d的区间。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2000010;
inline ll read()
{
    ll re=0;char ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') re=re*10+(ch^48),ch=getchar();
    return re;
}

int n=read(),p=read();ll d=read();
ll s[maxn],t[maxn];//t记录长度为d的一段区间的和
int l,r,q[maxn];

int main()
{
    for(int i=1;i<=n;i++) s[i]=s[i-1]+read();
    for(int i=d;i<=n;i++) t[i]=s[i]-s[i-d];//debug i:1~n-d+1
    l=r=0;q[0]=d;
    int ans=d,last=1;
    for(int i=d+1;i<=n;i++)
    {
        while(l<=r && t[q[r]]<=t[i]) r--;
        q[++r]=i;
//        while(l<=r && q[l]-d+1<last) l++;
        while(s[i]-s[last-1]-t[q[l]]>p)
        {
            last++;
            if(l<=r && q[l]-d+1<last) l++;//debug q[l]-d+1才是开头 
        }
        ans=max(ans,i-last+1);
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/83108399