傳送門
- 題目:
- 思路:
- 式子:
earn[i]=a−c[i]sum[i]=j=1∑iearn[j],(1≤i≤n)ans=max{sum[r]−sum[l−1]+gap(l,r)}
- 純單調棧:O(n)
從後往前枚舉左端點:ans=max{sum[r]+gap(l,r)−sum[l−1]}左端點固定時,式子中的最後一項sum[l−1]是一定的,每當左端點向左移動一位時,g(l,r)可能會改變。
假定左端點向左移動一位時新增的差值爲sd,sd=d[l+1]−d[l],考慮以sd爲g(l,r)的右端點r,且在這些右端點中記錄最大的max[r],記爲val,那麼val−sd∗sd−earn[l]就可能成爲最大值,記爲A。同時也要記錄[l+1,r]內最大合法的的sum[r]+gap(l+1,r),記爲B,並取max(A,B)−earn[l−1]更新答案。
因爲是新值先處理的思路,所以用棧來做。注意l=r的情況,狀態要從合法的情況轉移過來。
- 單調棧+線段樹最大子段和:O(nlogn)
觀察式子ans=max{sum[r]−sum[l−1]+gap(l,r)},如果沒有最後一項,前面可以通過線段樹求[1,n]的最大子段和得到。有最後一項的話,枚舉差值sd,通過單調棧預處理出以當前位置的sd爲gap(l,r)的區間l,r,在l,r區間內查詢最大子段和,不斷更新答案,特殊處理下選擇l=r這種情況。
大佬詳解
- ac代碼:
純單調棧做法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+10;
int n;
ll a, c;
ll d[maxn], earn[maxn];
struct node{
ll sub, val, mx_all;
}s[maxn];
int main()
{
scanf("%d %lld", &n, &a);
for(int i = 1; i <= n; i++) scanf("%lld %lld", &d[i], &c), earn[i] = earn[i-1]+(a-c);
int top = 0;
ll ans = max(0ll, a-c);
s[++top] = {0, earn[n], earn[n]};
s[0].mx_all = LLONG_MIN;
for(int i = n-1; i >= 1; i--)
{
ll sd = d[i+1]-d[i], mx = LLONG_MIN;
while(top>0 && s[top].sub<=sd) mx = max(mx, s[top--].val);
top++; s[top] = {sd, mx, max(s[top-1].mx_all, mx-sd*sd)};
top++; s[top] = {0, earn[i], max(s[top-1].mx_all, earn[i])};
ans = max(ans, s[top].mx_all-earn[i-1]);
}
printf("%lld\n", ans);
return 0;
}