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;
}
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签