[KOI 2025 #2] 序列与查询
子问题 1
当给定一个查询时,可以固定
子问题 2
我们定义数组
当给定查询
子问题 3
我们定义
当我们在二维平面上绘制
子问题 6
快速计算数组
让我们考虑一个与子问题 3 的算法等价但效率稍低的方法:当在二维平面上给定
我们用分治法来解决这个问题。定义函数
某个点
当我们求出点集
分治算法由于在求凸包的过程中需要排序,因此需要
子问题 7
上述分治算法可以在
Remark 1. 由于输入限制较大,在使用 CCW 计算凸包等时,可能会发生 long long 范围溢出的问题。这个问题有四种不同的解决方法(按便利性递增的顺序列出)。示例代码是用第二种方法实现的。
- 不使用 CCW,而是使用比较斜率的函数。由于查询值都是整数,斜率可以用整数商进行不精确的比较,而不会产生问题。
- 用
double类型计算 CCW 的值,如果绝对值小于等于10^{18} ,则再用long long重新进行精确计算(允许溢出)。 - 用
long double类型计算 CCW 的值。 - 用
__int128类型计算 CCW 的值:本次比赛使用的 Biko 评测环境支持该类型。
Remark 2. 关于这个问题,先前已知的解法是 Sakai 在 2024 年的论文 [Sak24],其预处理时间为
References
[Sak24] Yoshifumi Sakai. A data structure for the maximum-sum segment problem with offsets. In 35th Annual Symposium on Combinatorial Pattern Matching (CPM 2024), pages 26-1. Schloss Dagstuhl-Leibniz-Zentrum für Informatik, 2024.
#include <bits/stdc++.h>
using namespace std;
using lint = long long;
using pi = array<lint, 2>;
#define sz(v) ((int)(v).size())
#define all(v) (v).begin(), (v).end()
#define cr(v, n) (v).clear(), (v).resize(n);
pi operator+(const pi &a, const pi &b) { return pi{a[0] + b[0], a[1] + b[1]}; }
pi operator-(const pi &a, const pi &b) { return pi{a[0] - b[0], a[1] - b[1]}; }
int ccw(pi a, pi b) {
double apx = 1.0 * a[0] * b[1] - 1.0 * a[1] * b[0];
if (fabs(apx) > 1e18)
return apx > 0 ? 1 : -1;
lint ans = a[0] * b[1] - a[1] * b[0];
if (ans == 0)
return 0;
return ans > 0 ? 1 : -1;
}
int ccw(pi a, pi b, pi c) { return ccw(b - a, c - a); }
struct cht {
vector<pi> ch;
bool bad(pi a, pi b, pi c) { return ccw(b - a, b - c) <= 0; }
void init(vector<pi> &a) {
for (auto &x : a) {
if (sz(ch) && ch.back()[0] == x[0])
continue;
while (sz(ch) >= 2 && bad(ch[sz(ch) - 2], ch.back(), x))
ch.pop_back();
ch.push_back(x);
}
}
lint query(lint x) {
int s = 0, e = sz(ch) - 1;
while (s != e) {
int m = (s + e) / 2;
if (ch[m][0] * x + ch[m][1] > ch[m + 1][0] * x + ch[m + 1][1])
e = m;
else
s = m + 1;
}
return ch[s][0] * x + ch[s][1];
}
} cht;
vector<lint> a, dap;
void dnc(int l, int r) {
if (l == r)
return;
int m = (l + r) / 2;
dnc(l, m);
dnc(m + 1, r);
vector<pi> L, R;
for (int j = m + 1; j <= r; j++) {
pi p{j, a[j]};
while (sz(R) >= 2 && ccw(R[sz(R) - 2], R.back(), p) >= 0)
R.pop_back();
R.push_back(p);
}
for (int j = m; j >= l; j--) {
pi p{-j, -a[j]};
while (sz(L) >= 2 && ccw(L[sz(L) - 2], L.back(), p) >= 0)
L.pop_back();
L.push_back(p);
}
pi beg = L[0] + R[0];
vector<pi> d1, d2;
for (int k = 0; k + 1 < sz(L); k++) {
d1.push_back(L[k + 1] - L[k]);
}
for (int k = 0; k + 1 < sz(R); k++) {
d2.push_back(R[k + 1] - R[k]);
}
vector<pi> delta(sz(d1) + sz(d2));
merge(all(d1), all(d2), delta.begin(), [&](const pi &a, const pi &b) { return ccw(a, b) < 0; });
for (int i = 0; i < sz(delta); i++) {
dap[beg[0] - 1] = max(dap[beg[0] - 1], beg[1]);
beg = beg + delta[i];
}
dap[beg[0] - 1] = max(dap[beg[0] - 1], beg[1]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
cr(a, n + 1);
cr(dap, n);
for (int i = 0; i < n; i++) {
lint z;
cin >> z;
a[i + 1] = a[i] + z;
}
fill(all(dap), -1e18);
dnc(0, n);
vector<pi> points;
for (int i = 0; i < n; i++) {
points.push_back({i + 1, dap[i]});
}
cht.init(points);
for (int i = 0; i < q; i++) {
lint x;
cin >> x;
cout << cht.query(x) << "\n";
}
}