P3594 [POI2015]WIL-Wilcze doły

P3594 [POI2015]WIL-Wilcze doły

題目描述
給定一個長度爲n的序列,你有一次機會選中一段連續的長度不超過d的區間,將裏面全部數字所有修改成0。請找到最長的一段連續區間,使得該區間內全部數字之和不超過p。ios

輸入格式:
第一行包含三個整數n,p,d(1<=d<=n<=2000000,0<=p<=10^16)。第二行包含n個正整數,依次表示序列中每一個數wi。spa


調試日誌: 開始 p 和 d 輸反了。。。查了半天、、老想本身哪裏想錯了QAQ3d


Solution

首先感性認識一下, 隨着某一特定的右端點的增長, 其對應的最優左端點是會增長的
也就是說左端點隨着右端點的單調遞增
貪心認識一下, 因爲元素大小大於0, 消去的能選滿必定選滿
因此處理出 \(maxx[i]\) 表示以 \(i\) 做爲起點, (在不越界的狀況下)選滿的值調試

由於左端點隨右端點單調遞增
咱們枚舉每個右端點, 同時單調隊列處理出現區間內的最大能消去值 \(M\)
如果仍 \(sum[i] - sum[now - 1] - M > p\) 那麼只好更新左端點了
每次更新 \(ans\) 便可日誌

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(LL i = (x);i <= (y);i++)
using namespace std;
LL RD(){
    LL out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const LL maxn = 4000019;
LL num, d, p;
LL a[maxn];
LL maxx[maxn];//以i開頭的滿長消去值
LL sum[maxn];
void init(){
    num = RD(), p = RD(), d = RD();
    REP(i, 1, num)a[i] = RD(), sum[i] = sum[i - 1] + a[i];
    REP(i, 1, num){
        LL now = i + d - 1 > num ? sum[num] : sum[i + d - 1];
        maxx[i] = now - sum[i - 1];
        }
    }
struct Que{
    LL val, Index;
    }Q[maxn];//存maxx
LL head = 1, tail = 0;
void push_back(LL v, LL i){
    while(head <= tail && Q[tail].val <= v)tail--;
    Q[++tail] = (Que){v, i};
    }
LL now;//目前真實隊列的隊頭
LL get_max(){
    while(head <= tail && Q[head].Index < now)head++;
    return Q[head].val;
    }
LL ans;
void solve(){
    now = 1;
    REP(i, d, num){
        push_back(maxx[i - d + 1], i - d + 1);
        LL M = get_max();
        while(sum[i] - sum[now - 1] - M > p)now++, M = get_max();
        ans = max(ans, i - now + 1);
        }
    printf("%lld\n", ans);
    }
int main(){
    init();
    solve();
    return 0;
    }

CG

相關文章
相關標籤/搜索