CF1580D Subsequence 題解

Rainsheep發表於2024-11-23

最值相關問題我們有常用的 最值分治/笛卡爾樹/單調棧 的經典做法,即處理出每個點作為最值能覆蓋的區間的左端點/右端點。

注意一個關鍵點:在笛卡爾樹上,任意兩個點在原序列上最小值的下標,為兩點在原樹上的 LCA。

因為笛卡爾樹是 BST,所以子樹內的下標連續,又因為笛卡爾樹是堆,所以根節點一定是最小值。

知道了這點之後,我們考慮列舉 LCA,並在 LCA 處統計貢獻。我們有 \(f_{u, j}\) 表示在 \(u\) 的子樹內選取了 \(j\) 個位置的最大貢獻。

轉移考慮樹形揹包,根據經典結論複雜度為 \(O(n^2)\)

// 如果命運對你緘默, 那就活給他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL; 
// #define int LL
const int maxn = 4010;
int m, a[maxn];
int n;
LL f[maxn][maxn];
namespace tr {
  int s[maxn][2];
  int st[maxn], t = 0;
  inline void build() {
    for(int i = 1; i <= n; ++ i) {
      int k = t;
      while(k && a[st[k]] > a[i]) k -- ;
      if(k) s[st[k]][1] = i;
      if(k < t) s[i][0] = st[k + 1];
      st[t = (++ k)] = i;
    }
  }
  int sz[maxn];
  LL g[maxn];
  inline LL cmx(LL& x, LL y) {
    if(y > x) x = y; return x;
  }
  inline void solve(int u) {
    sz[u] = 1;
    f[u][1] = 1LL * m * a[u] - a[u];
    for(int v : {s[u][0], s[u][1]}) {
      if(!v) continue ;
      solve(v);
      for(int j = 0; j <= sz[u] + sz[v]; ++ j) g[j] = f[u][j];
      for(int j = 0; j <= sz[u]; ++ j) {
        for(int k = 0; k <= sz[v]; ++ k) {
          cmx(g[j + k], (LL)f[u][j] + f[v][k] - 2LL * j * k * a[u]);
        }
      }
      sz[u] += sz[v];
      for(int j = 0; j <= sz[u]; ++ j) f[u][j] = g[j];
    }
  }
}
signed main() {
  // freopen(".in", "r", stdin);
  // freopen(".out", "w", stdout);
  ios :: sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  cin >> n >> m;
  for(int i = 1; i <= n; ++ i) cin >> a[i];
  tr :: build();
  tr :: solve(tr :: st[1]);
  cout << f[tr :: st[1]][m] << '\n';
  return 0; 
}

相關文章