FJD1T3(LOJ 2773 学习轨迹)

发现了\(FJOI\)原题
没什么想法,想到自己考场上连\(n^2\)做法都不会就很感慨。
考虑如果只选择一个序列的任务,那么肯定全部选择会更加优秀。
那么考虑如果我们选择了两个序列的一部分。
如果\(A\)中选择的部分的权值和小于\(A\)权值和的一半,且\(B\)也如此,那我们不如只选择一个序列来的优。
我们考虑强制一个序列\(A\)选择超过一半,然后再反过来做一遍。
那么考虑在\(A\)中第一个大于一半的位置\(p\),那么这个位置是一定要选上的,否则选择的序列全在左边,或全在右边,都不满足条件。
那么考虑维护这个\(B\)中的序列右端,则每次都只有一个新出现\(ban\)点,考虑用线段树维护\([1...r-1,r]\)这些序列的答案。
考虑用单调栈来维护,在每次新增 ban 掉的点时,不断将它能覆盖的点弹出,把原来的贡献删掉,在最后把现在的贡献一起加上.
任然觉得自己考场上太蠢了。
开始\(rush\)代码。

LOJ 2773 学习轨迹
//%std
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define y1 ysgh
inline int read()
{
	int out = 0, fh = 1;
	char jp = getchar();
	while ((jp > '9' || jp < '0') && jp != '-') 
		jp = getchar();
	if (jp == '-') fh = -1, jp = getchar();
	while (jp >= '0' && jp <= '9') 
		out = out * 10 + jp - '0', jp = getchar();
	return out * fh;
}
void print(int x)
{
	if (x >= 10) print(x / 10);
	putchar('0' + x % 10);
}
void write(int x, char c)
{
	if (x < 0) putchar('-'), x = -x;
	print(x);
	putchar(c);
}
const int N = 5e5 + 10;
int n, m, a[N], b[N], c[N << 1], p1, p2, p3, p4, swaped = 0;
ll ans, suma[N], sumb[N];
ll mx[N << 2], ext[N << 2];
void pushup(int x)
{
	mx[x] = max(mx[x << 1], mx[x << 1 | 1]) + ext[x];
}
void BuildTree(int x, int l, int r)
{
	ext[x] = 0;
	if (l == r)
	{
		mx[x] = suma[n] - sumb[l - 1];
		return;
	}
	int mid = (l + r) >> 1;
	BuildTree(x << 1, l, mid);
	BuildTree(x << 1 | 1, mid + 1, r);
	pushup(x);
}
void upd(int x, int l, int r, int L, int R, ll d)
{
	if (L <= l && r <= R)
	{
		mx[x] += d, ext[x] += d;
		return;
	}
	int mid = (l + r) >> 1;
	if (L <= mid)
		upd(x << 1, l, mid, L, R, d);
	if (R > mid)
		upd(x << 1 | 1, mid + 1, r, L, R, d);
	pushup(x);
}
int query(int x, int l, int r)
{
	if (l == r)
		return l;
	int mid = (l + r) >> 1;
	if (mx[x] == mx[x << 1] + ext[x])
		return query(x << 1, l, mid);
	return query(x << 1 | 1, mid + 1, r);
}
void report(ll val, int a, int b, int c, int d)
{
	if (val > ans)
	{
		if (swaped)
			swap(a, c), swap(b, d);
		ans = val, p1 = a, p2 = b, p3 = c, p4 = d;
	}
}
int tpl, tpr, sl[N], sr[N];
void solve()
{
	int p = lower_bound(suma + 1, suma + n + 1, (suma[n] + 1) >> 1) - suma;
	BuildTree(1, 1, m);
	tpl = tpr = 0;
	for (int i = 1; i <= m; ++i)
	{
		if (b[i])
		{
			if (b[i] <= p)
			{
				while (tpl && b[i] > b[sl[tpl]])
				{
					upd(1, 1, m, sl[tpl - 1] + 1, sl[tpl], suma[b[sl[tpl]]]);
					--tpl;
				}
				upd(1, 1, m, sl[tpl] + 1, i, -suma[b[i]]);
				sl[++tpl] = i;
			}
			else
			{
				while (tpr && b[i] < b[sr[tpr]])
				{
					upd(1, 1, m, sr[tpr - 1] + 1, sr[tpr], suma[n] - suma[b[sr[tpr]] - 1]);
					--tpr;
				}
				upd(1, 1, m, sr[tpr] + 1, i, suma[b[i] - 1] - suma[n]);
				sr[++tpr] = i;
			}
		}
		int j = query(1, 1, m);
		int pl = lower_bound(sl + 1, sl + tpl + 1, j) - sl;
		int pr = lower_bound(sr + 1, sr + tpr + 1, j) - sr;
		pl = pl <= tpl ? b[sl[pl]] + 1 : 1;
		pr = pr <= tpr ? b[sr[pr]] - 1 : n;
		report(sumb[i] + mx[1], pl, pr, j, i);
	}
}
int main()
{
	n = read(), m = read();
	for (int i = 1; i <= n; ++i) a[i] = read();
	for (int i = 1; i <= n; ++i) suma[i] = suma[i - 1] + read();
	for (int i = 1; i <= m; ++i) c[read()] = i;
	for (int i = 1; i <= m; ++i) sumb[i] = sumb[i - 1] + read();
	for (int i = 1; i <= n; ++i) a[i] = c[a[i]], b[a[i]] = i;
	report(suma[n], 1, n, 0, 0), report(sumb[m], 0, 0, 1, m);
	solve();
	swap(n, m), swap(a, b), swap(suma, sumb), swaped = 1;
	solve();
	printf("%lld\n", ans);
	printf("%d %d\n", p1, p2);
	printf("%d %d\n", p3, p4);
	return 0;
}
原文地址:https://www.cnblogs.com/dixiao/p/14661925.html