Gifts Order

题目大意

给定 \(1 \le n \le 2 \times 10^5\) 以及一个序列 \(a\).

现在有 \(1 \le q \le 2 \times 10^5\) 次修改, 每次将 \(a_p \gets x\), 你需要最大化下面的式子:

\[f(l, r) = \max^r_{i = l}(a_i) - \min^r_{i = l}(a_i) - (r - l) \]

其中 \(1 \le l \le r \le n\).

思路

先抛一个性质: 最大值和最小值一定在答案序列的两端.

证明一下, 如果最小值不在端点上, 那么我们一定可以缩小区间来减小 \((r - l)\) 从而加大 \(f(l, r)\), 最大值也同理.

那这样就好做了, 分两种情况讨论.

  1. 最小值在左, 最大值在右.

    $f(l, r) = a_l - a_r - (r - l) $, 移一下项, \(f(l, r) = a_l + l - a_r - r\).

    这样就可以用线段树维护两个值 \(a_i + i\)\(-a_i - i\) 了.

  2. 最大值在左, 最小值在右.

    同样地, \(f(l, r) = a_r - a_l - (r - l) = a_r - r - a_l + l\), 用线段树维护下 \(a_i - i\)\(-a_i + i\) 即可.

具体可以看实现, 时间复杂度 \(\mathcal{O}((n + q) \log n)\).

#include "iostream"
#include "algorithm"

using namespace std;

constexpr int N = 2e5 + 10;

int n, q, a[N];

#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r

class Segment_Tree {
protected:
	int t1[N << 2], t2[N << 2], t3[N << 2], t4[N << 2];
	
public:
	int ans[N << 2];
	
	void update(int rt, int l, int r, int x, int k) {
		if (l == r)
			return ans[rt] = 0, t1[rt] = k + l, t2[rt] = -k - l, t3[rt] = k - l, t4[rt] = -k + l, void();
		int mid = (l + r) >> 1;
		if (x <= mid)
			update(lson, x, k);
		else
			update(rson, x, k);
		ans[rt] = max({ans[rt << 1], ans[rt << 1 | 1], t1[rt << 1] + t2[rt << 1 | 1], t4[rt << 1] + t3[rt << 1 | 1]});
		t1[rt] = max(t1[rt << 1], t1[rt << 1 | 1]), t2[rt] = max(t2[rt << 1], t2[rt << 1 | 1]);
		t3[rt] = max(t3[rt << 1], t3[rt << 1 | 1]), t4[rt] = max(t4[rt << 1], t4[rt << 1 | 1]);
	}
	
} st;

void init() {
	scanf("%d %d", &n, &q);
	for (int i = 1; i <= n; ++i)
		scanf("%d", a + i), st.update(1, 1, n, i, a[i]);
}

void calculate() {
	printf("%d\n", st.ans[1]);
	while (q--) {
		int p, x;
		scanf("%d %d", &p, &x);
		st.update(1, 1, n, p, x);
		printf("%d\n", st.ans[1]);
	}
}

void solve() {
	init();
	calculate();
}

int main() {
	int t;
	scanf("%d", &t);
	while (t--)
		solve();
	return 0;
}
posted @ 2025-01-06 15:45  Steven1013  阅读(20)  评论(0)    收藏  举报