1. 程式人生 > >bzoj 4385 poi 2015 Wilcze doły 單調佇列

bzoj 4385 poi 2015 Wilcze doły 單調佇列

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。

 

來源/分類

POI2015 

 

[提交] [狀態]

題意讓選出最長的一段連續區間使得該區間內數的和不超過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;
}