题意:给定长为(n)的序列,定义区间([l,r])的权值(C(l,r))为([l,r])中所有元素的和,现在试选择(k)个区间([l_i, r_i](1leq l_ileq r_ileq n)),使得
$$sum_{1leq Lleq Rleq n} [C(L, R) - max_{Lleq l_i leq r_i leq R } C(l_i, r_i)]$$最小,对(k=1, 2, 3..., n*(n+1)/2)作出回答,且(nleq 9)
不妨假定已经选择(k)个区间,考虑怎么使得权值和最小
显然,将(k)个区间按权值大小从大到小排序,所有的([L, R])区间会按此顺序选择第一个被它包含的区间([l_i, r_i])
换而言之,即将(k)个区间从大到小排序后,依次考虑,在仍未选择的区间([L, R])中,所有能选择([l_i, r_i])的区间一定会选择此区间
可以(dp),状态为已经选择了(k)个区间,及仍未选择的区间
仍未选择的区间可以通过((r_1, r_2, ..., r_n))表示,其中(r_i)表示([i, i], [i,i+1], ..., [i, r_i])仍未选择
由于(r_1leq r_2 leq ... leq r_n),状态数并不大(最大375544),并且本题有6s时限,hash都不用,开个map即可过
#include <map>
#include <vector>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define l first
#define r second
#define ll long long
#define mp make_pair
#define pii pair <int, int>
#define rep(io, st, ed) for(int io = st; io <= ed; io ++)
#define drep(io, ed, st) for(int io = ed; io >= st; io --)
const int sid = 1e6 + 5;
int n, tt[105];
ll all, ans[105], s[105];
map <ll, ll> now, pre;
vector <pii> seg;
ll encode(int *a) {
ll ret = 0;
drep(i, n + 1, 1) ret = ret * 10 + a[i];
return ret;
}
void decode(ll v, int *a) {
rep(i, 1, n) a[i] = v % 10, v /= 10;
a[n + 1] = v;
}
int main() {
cin >> n;
rep(i, 1, n) cin >> s[i], s[i] += s[i - 1];
rep(L, 1, n) rep(R, L, n) seg.push_back(mp(L, R)), all += s[R] - s[L - 1];
sort(seg.begin(), seg.end(), [](pii &a, pii &b) { return s[a.r] - s[a.l - 1] >= s[b.r] - s[b.l - 1]; } );
rep(i, 1, n) tt[i] = n; now[encode(tt)] = 0;
rep(si, 0, seg.size() - 1) {
int l = seg[si].l, r = seg[si].r;
ll v = s[r] - s[l - 1];
pre = now; now.clear();
for(auto x : pre) {
ll pv = x.second; decode(x.first, tt);
//不选择
now[x.first] = max(now[x.first], pv);
//选择
rep(i, 1, l) if(tt[i] >= r) pv += (tt[i] - r + 1) * v, tt[i] = r - 1;
tt[n + 1] ++; ll to = encode(tt);
now[to] = max(now[to], pv);
}
}
for(auto x : now) {
decode(x.first, tt);
ans[ tt[n + 1] ] = max(ans[ tt[n + 1] ], x.second);
}
rep(i, 1, n * (n + 1) / 2) printf("%lld
", all - ans[i]);
return 0;
}