专栏文章
题解:P11958 「ZHQOI R1」划分
P11958题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @mipuen1s
- 此快照首次捕获于
- 2025/12/03 18:06 3 个月前
- 此快照最后确认于
- 2025/12/03 18:06 3 个月前
如果序列里面的数全正或者全负,则容易看出最优方案是不进行任何划分,输出序列中的最大值与最小值的乘积即可。
否则可以转化为:选择若干个不交的区间 ,最小化 。
证明:记原问题的答案为 ,转化后的问题的答案为 。对于序列的任何划分,我们可以对于划分的每一段找到序列中最小值和最大值的位置,并将这一段缩小到恰好以最小值和最大值为两个端点,这样就可以得到转化后的问题的一个方案。据此,我们有 。
对于转化后的问题的最优方案,显然其选择的每一个区间的两端点的正负性均相反,否则删除该区间一定更优。我们任意扩张区间,直到所有元素均被覆盖。在这个过程中,每一个区间的 会进一步减小(为绝对值更大的负值), 会进一步增大(为绝对值更大的正值),从而 一定会进一步减小。从而我们得到了一个答案更小的原问题的方案。据此我们有 。综上,。
转化后的问题容易使用 dp 解决:记 表示序列以 为结尾的前缀的最优答案,转移分类讨论 是否被一个区间覆盖:
- 不被区间覆盖:转移为 。
- 被区间覆盖:对所有 ,转移为 。
这个转移容易使用李超树优化:每次计算一个 就将直线 加入李超树即可。复杂度 。
CPP#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;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...