NKOJ 3775 数列操作(单调队列+DP)

来源:互联网 发布:ubuntu无法删除文件夹 编辑:程序博客网 时间:2024/05/20 11:27

P3775数列操作

问题描述

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

输入格式

第一行包含三个整数n,p,d(1<=d<=n<=300000,0<=p<=10^16)。
第二行包含n个正整数,依次表示序列中每个数wi。

输出格式

包含一行一个正整数,即修改后能找到的最长的符合条件的区间的长度。

样例输入

9 7 2
3 4 1 9 4 1 7 1 3

样例输出

5


由于要讨论区间和,所以先求出前缀和数组Sum[i],现在如果我们选择以j结尾的一个区间,那么有Sum[j]Sum[i](Sum[x]Sum[xd])<=pxd>=ix<=j
所求的区间就是[i+1,j]。那么我们需要找到满足条件的最小的i。
考虑用单调队列优化,由于Sum数组是单调递增的,因此用l记下当前讨论的区间的左端点,如果左端点不满足上述条件,那么右移,现在只需要找到当前区间中(Sum[x]Sum[xd])的最大值即可,这个用单调队列维护即可。然而我石乐志,用堆来维护。


代码:

#include<stdio.h>#include<iostream>#include<algorithm>#include<deque>#include<queue>#define ll long long#define N 333333using namespace std;struct node{ll v,x;};bool operator<(node a,node b){return a.v<b.v;}priority_queue<node>Q;deque<ll>P;node tmp;ll n,p,d,i,j,k,S[N],ans;int main(){    scanf("%lld%lld%lld",&n,&p,&d);    for(i=1;i<=n;i++)scanf("%lld",&k),S[i]=S[i-1]+k;    for(i=0;i<d;i++)P.push_back(i);    for(i=d;i<=n;i++)    {        tmp.v=S[i]-S[i-d];        tmp.x=i-d;        Q.push(tmp);        P.push_back(i);        while(P.size()&&p+S[P.front()]+Q.top().v<S[i])        {            P.pop_front();            while(Q.size()&&Q.top().x<P.front())Q.pop();        }        ans=max(ans,i-P.front());    }    cout<<ans;}
原创粉丝点击