BZOJ4385/POI2015Wilcze doły

思路:
如果碰到一题你没有什么思路,不妨先想想最终的答案是什么样子的。这个方法对一类构造题和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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章