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;
}
posted @ 2024-03-06 15:35  cxqghzj  阅读(18)  评论(0编辑  收藏  举报