洛谷 P1714 切蛋糕 单调队列

这个题比较显然,要用前缀和来做。但只用前缀和是过不去的,会TLE,所以需要进行优化。

对于每个前缀和数组 b 中的元素,都可以找到以 b[i] 结尾的子段最大值 p[i],显然,最终的 ans 就是 max(p[i]),其中 1 ≤ i ≤ n。

故可知,ans = max( p[i] ) = max( max( b[i] - b[j] ) ),其中的 max( b[i] - b[j] ) 是 p[i]。

很明显, p[i] = b[i] - min( b[j] ),其中 i-m ≤ j ≤ i-1

这时候的重点就是在小于 O(m) 的时间里找出最小的 b[j] ,若已知 min(b[j]) ,即可求出 p[i]。

因为一直要找最小的 b[j],而这个框框 m 就引得我们想到 单调队列

单调队列里面存的就是这个 “最小的b[j]” ,这样在打完表后就可以在 O(1) 的时间里查到它啦。

下面是AC代码

//P1714 切蛋糕
#include<iostream>
#include<cstdio>
#define NUM 500010
using namespace std;
int n,m;
long long p[NUM],b[NUM];//b是前缀和_
long long q1[NUM];//单调队列_
void xiao(){ //建立单调队列
    int head = 1,tail = 0;//head <= tail时队列里有值_
    for( int i = 1;i <= n;i++ ){
        while( head <= tail && q1[head] + m <= i ) head++; //如果这个head已经过期了,就直接在队头pop
        while( head <= tail && b[i] < b[q1[tail]] ) tail--;
        q1[++tail] = i;
        if( i >= m ) p[i] = b[q1[head]];
    }
}
int main(){
    ios::sync_with_stdio(0);//关同步,这样输入可以快一点(因为我不想打快读)
    cin >> n >> m;
    for( int i = 1;i <= n;i++ ){
        long long x;
        cin >> x;
        b[i] = b[i-1] + x;//数组b存前缀和
    }
    xiao();//建立单调队列
    long long ans = b[1];//因为我们下一行的i是从m开始枚举,要是直接ans = -1,就会在第6个测点卡住
    for( int i = m;i <= n;i++ )
        if( ans < b[i] - p[i] ) ans = b[i] - p[i];
    cout << ans;
    return 0;
}

 

posted @ 2021-08-24 08:57  little_sheep_xiaoen  阅读(85)  评论(0编辑  收藏  举报