【CF1107G】Vasya and Maximum Profit(單調棧/單調棧+線段樹最大子段和)

傳送門

  • 題目:
    在這裏插入圖片描述
  • 思路:
    • 式子:
      earn[i]=ac[i]earn[i]=a-c[i]sum[i]=j=1iearn[j],(1in)sum[i]=\sum_{j=1}^{i}earn[j],(1≤i≤n)ans=max{sum[r]sum[l1]+gap(l,r)}ans=max\{sum[r]-sum[l-1]+gap(l,r)\}
    • 純單調棧:O(n)O(n)
      從後往前枚舉左端點:ans=max{sum[r]+gap(l,r)sum[l1]}ans=max\{sum[r]+gap(l,r)-sum[l-1]\}左端點固定時,式子中的最後一項sum[l1]sum[l-1]是一定的,每當左端點向左移動一位時,g(l,r)g(l,r)可能會改變。
      假定左端點向左移動一位時新增的差值爲sd,sd=d[l+1]d[l]sd,sd=d[l+1]-d[l],考慮以sdsdg(l,r)g(l,r)的右端點rr,且在這些右端點中記錄最大的max[r]max[r],記爲valval,那麼valsdsdearn[l]val-sd*sd-earn[l]就可能成爲最大值,記爲A。同時也要記錄[l+1,r][l+1,r]內最大合法的的sum[r]+gap(l+1,r)sum[r]+gap(l+1,r),記爲B,並取max(A,B)earn[l1]max(A,B)-earn[l-1]更新答案。
      因爲是新值先處理的思路,所以用棧來做。注意l=rl=r的情況,狀態要從合法的情況轉移過來。
    • 單調棧+線段樹最大子段和:O(nlogn)O(nlogn)
      觀察式子ans=max{sum[r]sum[l1]+gap(l,r)}ans=max\{sum[r]-sum[l-1]+gap(l,r)\},如果沒有最後一項,前面可以通過線段樹求[1,n][1,n]的最大子段和得到。有最後一項的話,枚舉差值sdsd,通過單調棧預處理出以當前位置的sdsdgap(l,r)gap(l,r)的區間l,rl,r,在l,rl,r區間內查詢最大子段和,不斷更新答案,特殊處理下選擇l=rl=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()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章