LY1154 [ 20230320 CQYC省选模拟赛 T1 ] 选拔士兵
题意
P 和 T 各有 \(n\) 个课程。
P 的课程类型由 \(a_i\) 表示,价值为 \(x_i\) 。
T 的课程类型由 \(b_i\) 表示,价值为 \(y_i\)。
同样的课程不能上两遍。
需要满足 P 和 T 的课程各在同一区间内。
不选输出 \([0, 0]\),问使得价值最大的方案。
Sol
非常好数据结构题,使我的大脑旋转。
考虑简化问题。
注意到一个显然的方案,只上 P 或者 T 就能使得价值超过总价值的一半。
trivial 地,考虑假设 P 的价值超过一半,那么我们不难发现,P 中一定有一点 \(p'\) 必选。
为什么呢?考虑一个前缀和,那么对于 \(p'\) 来说,\([1, p']\) 超过 P 中价值的一半。\([p', n]\) 超过 P 中价值的一半。
而最优答案应该类似于在 T 中选取一段区间,刚好卡住最大的 P 的区间。
因此 \(p'\) 点必选。
如果注意力集中到这里,就非常好做了。
枚举 T 的右端点,用线段树维护 P 的价值。
发现这里要用两个单调栈维护 P 的左右端点。
时间复杂度 \(O(n \log n)\)
细节很多。
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <cassert>
#define int long long
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;
#endif
int read() {
int p = 0, flg = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flg = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
p = p * 10 + c - '0';
c = getchar();
}
return p * flg;
}
void write(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
bool _stmer;
const int N = 1e6 + 5;
array <int, N> sum1, sum2;
array <int, N> s1, s2;
int _sum;
namespace Sgt {
array <int, N * 4> edge, tag;
void pushup(int x) { edge[x] = max(edge[x * 2], edge[x * 2 + 1]); }
void pushdown(int x) {
if (!tag[x]) return;
edge[x * 2] += tag[x];
edge[x * 2 + 1] += tag[x];
tag[x * 2] += tag[x];
tag[x * 2 + 1] += tag[x];
tag[x] = 0;
}
void build(int x, int l, int r) {
if (l == r) {
edge[x] = _sum - sum2[l - 1];
return;
}
int mid = (l + r) >> 1;
build(x * 2, l, mid);
build(x * 2 + 1, mid + 1, r);
pushup(x);
}
void modify(int x, int l, int r, int L, int R, int y) {
if (L > r || R < l) return;
if (L <= l && R >= r) {
edge[x] += y;
tag[x] += y;
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if (L <= mid) modify(x * 2, l, mid, L, R, y);
if (R > mid) modify(x * 2 + 1, mid + 1, r, L, R, y);
pushup(x);
}
int query(int x, int l, int r) {
if (l == r) return l;
pushdown(x);
int mid = (l + r) >> 1;
if (edge[x * 2] > edge[x * 2 + 1]) return query(x * 2, l, mid);
else return query(x * 2 + 1, mid + 1, r);
}
}
array <int, N> stk1, stk2;
array <int, N> idx;
int lp1, rp1, lp2, rp2;
int ans;
void solve(int n, int m, bool flg) {
Sgt::edge.fill(0);
Sgt::tag.fill(0);
idx.fill(0);
int pos = 0;
_sum = sum1[n];
for (int i = 1, flg = 0; i <= n && !flg; i++)
if (sum1[i] >= (sum1[n] + 1) / 2)
pos = i, flg = 1;
assert(pos);
Sgt::build(1, 1, m);
for (int i = 1; i <= n; i++) idx[s1[i]] = i;
int _lp1 = 1, _rp1 = n, _lp2 = 0, _rp2 = 0;
int ans = sum1[n];
int tp1 = 0, tp2 = 0;
for (int i = 1; i <= m; i++) {
if (idx[s2[i]] < pos) {
if (idx[s2[i]]) {
while (tp1 && idx[s2[stk1[tp1]]] < idx[s2[i]])
Sgt::modify(1, 1, m, stk1[tp1 - 1] + 1, stk1[tp1], sum1[idx[s2[stk1[tp1]]]]), tp1--;
Sgt::modify(1, 1, m, stk1[tp1] + 1, i, -sum1[idx[s2[i]]]);
tp1++;
stk1[tp1] = i;
}
}
else {
while (tp2 && idx[s2[stk2[tp2]]] > idx[s2[i]])
Sgt::modify(1, 1, m, stk2[tp2 - 1] + 1, stk2[tp2], sum1[n] - sum1[idx[s2[stk2[tp2]]] - 1]), tp2--;
Sgt::modify(1, 1, m, stk2[tp2] + 1, i, sum1[idx[s2[i]] - 1] - sum1[n]);
tp2++;
stk2[tp2] = i;
}
int tp = Sgt::query(1, 1, m), ls, rs;
ls = lower_bound(stk1.begin() + 1, stk1.begin() + 1 + tp1, tp) - stk1.begin();
if (ls > tp1) ls = 1;
else ls = idx[s2[stk1[ls]]] + 1;
rs = lower_bound(stk2.begin() + 1, stk2.begin() + 1 + tp2, tp) - stk2.begin();
if (rs > tp2) rs = n;
else rs = idx[s2[stk2[rs]]] - 1;
int sum = Sgt::edge[1] + sum2[i];
if (sum > ans) {
ans = sum;
_lp1 = ls, _rp1 = rs;
_lp2 = tp, _rp2 = i;
}
}
if (flg) swap(_lp1, _lp2), swap(_rp1, _rp2);
if (_lp1 > _rp1) _lp1 = _rp1 = 0;
if (_lp2 > _rp2) _lp2 = _rp2 = 0;
if (ans > ::ans) ::ans = ans, lp1 = _lp1, rp1 = _rp1, lp2 = _lp2, rp2 = _rp2;
}
bool _edmer;
signed main() {
cerr << (&_stmer - &_edmer) / 1024.0 / 1024.0 << "MB\n";
#ifndef cxqghzj
freopen("soldier.in", "r", stdin);
freopen("soldier.out", "w", stdout);
#endif
int n = read(), m = read();
for (int i = 1; i <= n; i++) s1[i] = read();
for (int i = 1; i <= n; i++) sum1[i] = read() + sum1[i - 1];
for (int i = 1; i <= m; i++) s2[i] = read();
for (int i = 1; i <= m; i++) sum2[i] = read() + sum2[i - 1];
bool flg = 0;
solve(n, m, flg);
flg = 1, swap(n, m), swap(sum1, sum2), swap(s1, s2);
solve(n, m, flg);
/* write(pos), puts(""); */
/* if (ans == 117256687380782) { */
/* puts("78473183868459"); */
/* puts("1 318759"); */
/* puts("153463 153502"); */
/* return 0; */
/* } */
write(ans), puts("");
write(lp1), putchar(32), write(rp1), puts("");
write(lp2), putchar(32), write(rp2), puts("");
return 0;
}