【大联盟】20230703 T2 开心的序列(sequence) 题解 AT_agc049_f 【[AGC049F] Happy Sequence】

发布时间 2023-07-22 10:47:13作者: zhaohaikun

zak /bx

恐怖 zak 将这题加强,出到模拟赛。直接把 \(A_i,B_i\le 10^5, C_i\le 5\) 变成了 \(A_i,B_i,C_i\le 10^9\)

非常恐怖。

题目描述

here

题解

重新再理解一遍。

我们维护 \(p(x)=\sum_i|a_i-x|+|b_i-x|\),那么就相当于要求 \(\forall x, p(x)\le 0\),也就是 \(p_{\max}\le 0\)

继续观察下。首先,显然需要满足 \(\sum_{i}a_i=\sum_{i}b_i\),我们设这个数为 \(S\)。那么,对于 \(\forall x, p(x)\le 0\) 的限制,我们设 \(ca\)\(<x\)\(a_i\) 个数、\(sa\)\(<x\)\(a_i\) 的和、\(cb\)\(sb\) 同理。

于是就得出,\(\forall x,(S-sa)-x(n-ca)+xca-sa\le (S-sb)-x(n-cb)+xcb-sb\),化简可得 \(\forall xca-sa\le xcb-sb\),也就是要求 \(\max\{xca-sa-(xcb-sb)\}\le 0\)

考虑 \(\max\) 取在哪?一定是在 \(ca=cb\) 的位置,因为若 \(ca<cb\),则 \(x+1\) 一定更优,若 \(ca>cb\),则 \(x-1\) 一定更优。

所以,我们只需要关注 \(ca=cb\) 的位置,于是得出结论:满足条件当且仅当,对于排好序\(a\)\(b\),满足 \(\forall 1\le x\le n,\sum_{i=1}^{x}a_i-b_i\ge 0\),且 \(\sum_{i=1}^{n}a_i=\sum_{i=1}^{n}b_i\)

我们令 \(g(x)=\sum_{i=1}^{x}a_i-b_i\),条件为 \(\forall g(x)\ge 0\)

这个结论赛时猜结论打表也发现了,不过完全没想到后面的。


我们发现:

  • \(a_i\) 增加 \(1\),就相当于给 \(x\le a_i\)\(p(x)\) 都增加 \(1\),给 \(x>A_i\)\(p(x)\) 都减少 \(1\)
  • \(a_i\) 减少 \(1\),就相当于给 \(x\ge a_i\)\(p(x)\) 都增加 \(1\),给 \(x<A_i\)\(p(x)\) 都减少 \(1\)

于是,我们观察到让 \(A\) 小的数减少 \(1\),并让 \(A\) 大的数增加 \(1\),是一定不优的,假设增加的数为 \(a\),减少的数为 \(b\),并满足 \(a<b\),则对于 \(x< a\)\(x>b\)\(p(x)\) 都是不变的,而对于 \(a\le x\le b\)\(p(x)\) 却增加了 \(2\)

所以,一定存在一个 \(mid\),满足对于 \(mid\) 左侧的数都在增加,对于 \(mid\) 右侧的数都在减小。如果有多个 \(mid\),取 \(p(mid)\) 最大的(假设左侧最右的操作为 \(l\),右侧最左的操作为 \(r\),则 \(mid\in(l,r)\))。

观察到操作后 \(p(mid)\ge -1\),因为如果不满足条件,则可以通过删除 左侧第一个操作或右侧第一个操作,这样 \(p(mid)\) 就增加 \(2\) 了,仍然满足条件还,还更优。

不过,进一步地,由于需要满足 \(\sum_{i=1}^{n}a_i=\sum_{i=1}^{n}b_i\),则说明 \(p(x)\) 一定为偶数。因为 \(p(x)=\sum_{i=1}^{n}(-1)^{[a_i>x]+1}a_i+(-1)^{[a_i>x]}x-\sum_{i=1}^{n}(-1)^{[b_i>x]+1}b_i+(-1)^{[b_i>x]}x\)

\[\begin{aligned}p(x)&\equiv \sum_{i=1}^{n}a_i+x-\sum_{i=1}^{n}b_i+x\pmod 2\\&\equiv \sum_{i=1}^{n}a_i-\sum_{i=1}^{n}b_i\pmod 2\\&\equiv 0\pmod 2\end{aligned} \]

所以,可以得到 \(p(mid)=0\)

然后,我们发现,由于 \(mid\) 左侧的数都在增加,\(mid\) 右侧的数都在减小。所以,每次操作都会让 \(p(mid)\) 变小,而 \(p(mid)=0\),说明初始时 \(p(mid)\) 是最大值。

带入 \(p(-\infty),p(\infty)\ge 0\),可以得到 \(p(-\infty)=p(\infty)=0\),又因为 \(p(mid)=0\),则说明左侧和相等,右侧和相等。

于是,左右就独立了,我们对左右分开做,形式相似(实际上只要把右侧取相反数就跟左侧形式完全一样了)。我们可以二分斜率 \(M\),不过可能出现不合法,那么我们跟上面一样,找到 \(mid\) 来处理。

\(mid\) 左侧的斜率调整为 \([mid,r]\),右侧调整为 \([l,mid]\),像整体二分一样。

时间复杂度 \(O(n\log n(\log A+\log B+\log C))\)

代码

记录

#include <bits/stdc++.h>
#define SZ(x) (int) x.size() - 1
#define all(x) x.begin(), x.end()
#define ms(x, y) memset(x, y, sizeof x)
#define F(i, x, y) for (int i = (x); i <= (y); i++)
#define DF(i, x, y) for (int i = (x); i >= (y); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T> void chkmax(T& x, T y) { x = max(x, y); }
template <typename T> void chkmin(T& x, T y) { x = min(x, y); }
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) { putchar('-'); x *= -1; }
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
const int N = 2e5 + 10;
int n, b[N], g[N];
struct node {
	ll cur;
	int beg, val;
	friend bool operator < (const node &x, const node &y) {
		return x.cur < y.cur;
	}
} t[N];
__int128 ans;
void solve(int l, int r, ll L, ll R) {
	if (l > r) return;
	ll mid = (L + R) >> 1;
	F(i, l, r) t[i].cur = t[i].beg + (mid / t[i].val + 1) / 2;
	sort(t + l, t + r + 1);
	ll s = 0; int pos = r + 1;
	DF(i, r, l) {
		s += b[i] - t[i].cur;
		if (s < 0) {
			s = 0;
			pos = i;
		}
	}
	if (L + 1 == R) {
		__int128 s = 0;
		F(i, l, r) {
			s += t[i].cur - b[i];
			ans += (__int128) (t[i].cur - t[i].beg) * (t[i].cur - t[i].beg) * t[i].val;
		} ans -= (__int128) s * R;
		return;
	} solve(l, pos - 1, mid, R), solve(pos, r, L, mid);
}
signed main() {
	// freopen("sequence.in", "r", stdin);
	// freopen("sequence.out", "w", stdout);
	read(n);
	F(i, 1, n) read(t[i].beg), t[i].cur = t[i].beg;
	F(i, 1, n) read(b[i]);
	sort(b + 1, b + n + 1);
	F(i, 1, n) read(t[i].val);
	sort(t + 1, t + n + 1);
	ll s = 0; int pos = 0;
	F(i, 1, n) {
		s += t[i].beg - b[i];
		if (s < 0) {
			s = 0;
			pos = i;
		}
	}
	solve(1, pos, 0, 2e18 + 5);
	reverse(t + pos + 1, t + n + 1);
	reverse(b + pos + 1, b + n + 1);
	F(i, pos + 1, n) t[i].beg = 1e9 - t[i].beg, b[i] = 1e9 - b[i];
	solve(pos + 1, n, 0, 2e18 + 5);
	write(ans);
	return 0;
}