CF1580D Subsequence 题解

最值相关问题我们有常用的 最值分治/笛卡尔树/单调栈 的经典做法,即处理出每个点作为最值能覆盖的区间的左端点/右端点。

注意一个关键点:在笛卡尔树上,任意两个点在原序列上最小值的下标,为两点在原树上的 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; 
}
posted @   Rainsheep  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签
点击右上角即可分享
微信分享提示