10319: Wilcze doły
时间限制: 1 Sec 内存限制: 128 MB
提交: 69 解决: 24
[提交] [状态] [讨论版] [命题人:admin]
题目描述
给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0。
请找到最长的一段连续区间,使得该区间内所有数字之和不超过p。
输入
第一行包含三个整数n,p,d(1<=d<=n<=2000000,0<=p<=10^16)。
第二行包含n个正整数,依次表示序列中每个数w[i](1<=w[i]<=10^9)。
输出
包含一行一个正整数,即修改后能找到的最长的符合条件的区间的长度。
样例输入
复制样例数据
9 7 2 3 4 1 9 4 1 7 1 3
样例输出
5
提示
将第4个和第5个数修改为0,然后可以选出区间[2,6],总和为4+1+0+0+1=6。
来源/分类
题意让选出最长的一段连续区间使得该区间内数的和不超过p,条件是可以把一段长度不超过d的连续区间里的数字全部修改为0
第一眼看到,没思路,后来想了想因为数据2e6,只能O(n),又因为要维护区间的最大值,所以想到单调队列
但是因为以前接触的单调队列都是确定长度的滑动区间,这个找最长就有点懵,觉得自己思路可能错了
后来上网搜题解,看到确实是单调队列,然后发现思路有点问题
单调队列维护的是d长度的最大区间,所以长度是已知,求最长长度的问题就可以囊括在里面
假设区间i到j之间是维护的该区间,那么该区间的和减去区间内d长度的和之后大于p,说明该区间不可行,那么左端点i+1
说的貌似有点乱,但是思路是单调队列没错的
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e6+100;
ll s[maxn];
ll f[maxn];
int in[maxn];
int main(){
int n,d;
ll p,a;
scanf("%d%lld%d",&n,&p,&d);
for(int i=1; i<=n; i++){
scanf("%lld",&a);
s[i]=s[i-1]+a;
}
for(int i=d; i<=n; i++){
f[i]=s[i]-s[i-d];
}
int tail=0,head=0;
int ans=d,i=0;
for(int j=d; j<=n; j++){
while(head<=tail && f[j]>=f[in[tail]]) tail--;
in[++tail]=j;
while(s[j]-s[i]-f[in[head]]>p){
i++;
while(head<=tail && in[head]-d<i) head++;
}
ans=max(ans,j-i);
}
printf("%d\n",ans);
return 0;
}