题解:P11958 「ZHQOI R1」划分
如果序列里面的数全正或者全负,则容易看出最优方案是不进行任何划分,输出序列中的最大值与最小值的乘积即可。
否则可以转化为:选择若干个不交的区间
证明:记原问题的答案为
对于转化后的问题的最优方案,显然其选择的每一个区间的两端点的正负性均相反,否则删除该区间一定更优。我们任意扩张区间,直到所有元素均被覆盖。在这个过程中,每一个区间的
转化后的问题容易使用 dp 解决:记
这个转移容易使用李超树优化:每次计算一个
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n;
long long a[N], f[N];
struct Line {
long long k, b;
Line() {}
Line(long long k, long long b) : k(k), b(b) {}
inline long long Eval(long long x) {return k * x + b;}
};
struct Node {
Line l;
Node *ls, *rs;
Node() {}
};
Node nd[N * 2];
int top;
struct Segtree {
Node *_root;
//isMin = 1 => min, isMin = 0 -> max
inline void Ins(Node *&p, long long pl, long long pr, Line nl) {
if (!p) {
p = &nd[++top];
p->l = nl;
return;
}
long long mid = (pl + pr) >> 1;
if (p && nl.Eval(mid) < p->l.Eval(mid)) swap(nl, p->l);
if (pl == pr) return;
if (nl.k < p->l.k) Ins(p->rs, mid + 1, pr, nl);
else Ins(p->ls, pl, mid, nl);
}
inline long long Query(Node *&p, long long pl, long long pr, long long x) {
if (!p) return 3e18;
long long mid = pl + pr >> 1;
if (mid >= x) return min(p->l.Eval(x), Query(p->ls, pl, mid, x));
else return min(p->l.Eval(x), Query(p->rs, mid + 1, pr, x));
}
};
Segtree sgt;
const int L = -1e6, R = 1e6;
int main() {
std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n;
bool fp = 0, fn = 0;
for (int i = 1;i <= n;i++) {
cin >> a[i];
if (a[i] > 0) fp = 1;
if (a[i] < 0) fn = 1;
}
if (!fp || !fn) {
long long mx = -1e9, mn = 1e9;
for (int i = 1;i <= n;i++) {
mx = max(mx, a[i]);
mn = min(mn, a[i]);
}
cout << mx * mn << endl;
} else {
f[0] = 0;
for (int i = 1;i <= n;i++) {
f[i] = min(f[i - 1], sgt.Query(sgt._root, L, R, a[i]));
sgt.Ins(sgt._root, L, R, Line(a[i], f[i - 1]));
}
cout << f[n] << endl;
}
return 0;
}