1. 程式人生 > >【[POI2015]WIL-Wilcze doły】

【[POI2015]WIL-Wilcze doły】

第一篇題解確實會被討論區裡的資料hack掉,那麼就隨便水一個不會被hack掉的題解吧

首先我們嘗試著發現這道題的一些結論,你就會發現答案是單調的不降的

這裡的答案不降指的是選擇每一個位置\(i\)作為結尾能形成的最長區間的左端點是單調不降的,這個很好證明,將\(i-1\)這個位置作為結尾形成的最長區間的左端點不可能比\(i\)作為結尾形成的最長區間的左端點更靠右

如果更靠右的話,那麼\(i-1\)形成的區間還能更靠左一些,這與我們的假設不符,所以這個結論是成立的

之後我們就可以利用這個結論計算每一個\(i\)為結尾的區間的左端點在哪裡了

由於\(i\)的左端點不可能比\(i-1\)的更靠左,所以我們就直接來將\(i-1\)

的左端點\(last\)為起始端點就好了

如果\(p[i]-p[last-1]\)即這段區間的和減去這個區間內所有長度為\(d\)的區間和的最大值還是超過\(p\),那麼我們就讓\(last++\),直到滿足條件為止

至於怎麼維護一個區間內所有長度為\(d\)的區間和的最大值,我們用一個單調佇列就好了

時間複雜度其實是均攤了兩次,但是還是非常優秀的\(O(n)\)

程式碼

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#define re register
#define maxn 2000005
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define LL unsigned long long
LL n,p,d;
LL a[maxn],pre[maxn];
LL ans,t[maxn],last;
inline LL read()
{
    LL x=0;
    char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9')
      x=x*10+c-48,c=getchar();
    return x;
}
int main()
{
    n=read();
    p=read();
    d=read();
    for(re int i=1;i<=n;i++)
        a[i]=read();
    for(re int i=1;i<=n;i++)
        pre[i]=pre[i-1]+a[i];
    std::deque<int> q;
    for(re int i=d;i<=n;i++)
        t[i]=pre[i]-pre[i-d];//t[i]表示[i-d+1,i]這個區間的和
    ans=d;//最開始ans為d
    q.push_back(d);
    last=1;
    for(re int i=d+1;i<=n;i++)
    {
        while(!q.empty()&&t[i]>t[q.back()]) q.pop_back();
        q.push_back(i);//在隊尾加入一個元素
        while(!q.empty()&&q.front()-d+1<last) q.pop_front();
        //,如果隊首元素的左端點比last還小,那麼就彈出不合法的隊首元素
        while(!q.empty()&&pre[i]-pre[last-1]-t[q.front()]>p)
        {
            last++;
            while(!q.empty()&&q.front()-d+1<last) q.pop_front();
            //last++後也要維護隊首的合法性
        }
        ans=max(ans,i-last+1);
    }
    std::cout<<ans;
    return 0;
}