洛谷 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; }