P12508 「ROI 2025 Day2」程序员的日常
在天数 \(k\) 固定时,定义 \(p_i\) 为第 \(i\) 个连续段的起点。那么一个贪心是在保证第 \(i\) 段的 \(\max=a_{p_i}\) 时尽量最小化 \(a_{p_{i+1}}\)。于是有 \(p_{i+1}=\arg\min\limits\left\{a_j\mid p_i+1\le j\le \min(r_{p_i},n-k+i+1)\right\}\)。注意最后一个位置可能要特判。
对于原题,考虑对 \(k\) 从小往大扫,动态维护所有的 \(p\)。每次 \(k\) 增大只会让 \(n-k+i+1\) 减小 \(1\),如果这使 \(p_{i+1}\) 变化,那么 \(p_{i+1}=n-k+i+1\),于是之后的 \(j\ge i+1\) 也有 \(p_j=n-k+j\)。
考虑维护出所有的连续段,每次只会删一段,我们只要支持快速加入一段。注意到从 \(p_i\) 推出 \(p_{i+1}\) 时,\(p_{i+1}\) 只和 \(k-i\) 有关,且 \(p_{i+1}=p_i+1\) 需要 \(k-i\) 不小于一个阈值 \(lim_i\)。这个 \(lim\) 是可以预处理的。找一段的右端点二分即可。
总复杂度 \(O(n\log n)\)。
#include <bits/stdc++.h>
using namespace std;
template <typename T> void Chkmax(T &x, T y) { x = max(x, y); }
const int kN = 3e5 + 5, kLog = 20;
int n, q, c;
int a[kN], lim[kN];
int ri[kN], suf[kN];
int qk[kN], qi[kN], ans[kN];
vector<int> qry[kN];
struct ST1 {
int mn[kLog][kN];
int Min(int x, int y) { return (a[x] < a[y]) ? x : y; }
void Build() {
iota(mn[0] + 1, mn[0] + n + 1, 1);
for(int i = 1; i < kLog; i++) {
for(int l = 1; l + (1 << i) - 1 <= n; l++) {
mn[i][l] = Min(mn[i - 1][l], mn[i - 1][l + (1 << i - 1)]);
}
}
}
int ArgMin(int l, int r) {
int p = __lg(r - l + 1);
return Min(mn[p][l], mn[p][r - (1 << p) + 1]);
}
}st1;
struct ST2 {
int mx[kLog][kN];
void Build() {
memcpy(mx[0], lim, sizeof(mx[0]));
for(int i = 1; i < kLog; i++) {
for(int l = 0; l + (1 << i) - 1 <= n; l++) {
mx[i][l] = max(mx[i - 1][l], mx[i - 1][l + (1 << i - 1)]);
}
}
}
int FindR(int x, int v) {
for(int i = kLog - 1; ~i; i--) {
if(x + (1 << i) - 1 > n) continue;
if(mx[i][x] <= v) x += (1 << i);
}
return x - 1;
}
}st2;
void Init() {
stack<int> stk;
for(int i = 0; i <= n + 1; stk.push(i++)) {
while(stk.size()) {
int top = stk.top();
if(a[top] < a[i]) ri[top] = i, stk.pop();
else break;
}
}
for(int i = n; i; i--) suf[i] = max(suf[i + 1], a[i]);
}
struct Node {
int l, r, v;
Node() { }
Node(int _l, int _r, int _v) {
l = _l;
r = _r;
v = _v;
}
};
Node seg[kN];
int main() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; i++) cin >> a[i];
a[n + 1] = n + 1;
Init();
st1.Build();
for(int i = 1; i <= n; i++) {
int l = i + 1, r = ri[i] + 1;
while(l + 1 < r) {
int mid = (l + r) >> 1;
(st1.ArgMin(i + 1, mid) == i + 1) ? (l = mid) : (r = mid);
}
if(r <= ri[i]) lim[i] = n + i - r + 2;
}
st2.Build();
for(int i = 1; i <= q; i++) {
cin >> qk[i] >> qi[i];
qry[qk[i]].push_back(i);
if(qk[i] == 1) ans[i] = n;
if(qk[i] == n) ans[i] = a[qi[i]];
}
seg[c = 1] = Node(0, 0, 0);
for(int k = 2; k < n; k++) {
int t = k - 2;
while(c && (seg[c].v > n - k)) t = seg[c--].l - 1;
for(int i = t; i < k; i++) {
int pi = seg[c].v;
if(pi == n - k) {
seg[++c] = Node(i + 1, k - 1, pi);
break;
}
if(lim[pi + i] <= pi + k) {
int pos = st2.FindR(pi + i, pi + k) - pi;
seg[++c] = Node(i + 1, pos + 1, pi);
i = pos;
continue;
}
int tp = pi + i;
int pn = st1.ArgMin(tp + 1, min(n - k + i + 1, ri[tp])) - i - 1;
seg[++c] = Node(i + 1, i + 1, pn);
}
for(int i : qry[k]) {
int t = min(qi[i], k - 1);
int l = 0, r = c + 1;
while(l + 1 < r) {
int mid = (l + r) >> 1;
(seg[mid].r < t) ? (l = mid) : (r = mid);
}
int res = seg[r].v + t;
if(qi[i] < k) ans[i] = a[res];
else ans[i] = suf[min(ri[res], n)];
}
}
for(int i = 1; i <= q; i++) cout << ans[i] << "\n";
return 0;
}
浙公网安备 33010602011771号