思路:
如果碰到一題你沒有什麼思路,不妨先想想最終的答案是什麼樣子的。這個方法對一類構造題和dp都很好用。
首先假設我們已經有了一個右端點,如果要使用修改的話,我們肯定是想讓左端點儘量的靠前,那麼這就要求我們要讓這次修改的收益最大化就是選個最大的區間將它的權值修改爲0 這樣等下可以利用這些鬆弛儘量左邊的點。具體說就是如果當前這個區間是[l,r],那麼我們要儘量選擇右端點範圍是[l,r]的長度爲d的區間使它的值最大(一些邊界問題自己考慮)然後就差不多解決了。
一開始想寫一個線段樹查詢。。但是後來發現這個是單調的 也就是說最終的左區間一定是隨着右區間遞增而遞增..所以單調隊列維護即可。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<ctime>
#define d(a) while(a(isdigit(ch=getchar())))
template<class T> T get(T &tmp){char ch;d(!);tmp=ch-'0';d() tmp=tmp*10+ch-'0';return tmp;}
using namespace std;
typedef long long LL;
const int imax=2000000+229;
int n,d;
LL a[imax],P,sum[imax],Max[imax];
void iread()
{
scanf("%d%lld%d",&n,&P,&d);
for(int i=1;i<=n;i++) get(a[i]);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
}
int q[imax];
void iwork()
{
for(int i=1;i<=n;i++)
{
int l=i; int r=min(l+d-1,n);
Max[i]=sum[r]-sum[l-1];
}
int head=1; int tail=0;
int len=d;
q[++tail]=1;
int ans=1;
for(int i=d+1;i<=n;i++)
{
int now=i-d+1;
while(tail>=head && Max[now]>=Max[q[tail]]) tail--;//注意考慮tail 與head的關係
q[++tail]=now;
while(sum[i]-sum[ans-1]>P+Max[q[head]])
{
ans++;
while(q[head]<ans) head++;
}
len=max(len,i-ans+1);
}
printf("%d\n",len);
}
int main()
{
iread();
iwork();
return 0;
}