20250807 做题记录

上午模拟赛过了 弹射器我们

下午补题过了 没有重复颜色,写了如下题解:

首先考虑这个“没有重复颜色的区间”应该满足什么条件。

发现,处理出每一个位置的左边,上一个同色的位置,记为 \(lst[i]\)。则,右端点为 \(i\),左端点要求 \(\ge\max_{j\in[1,i]}lst[j]\),记其为 \(L\)

考虑询问,每次询问一个区间的时候,我们查询 \([l,r]\) 的右端点的答案,并限制这些点的 \(L\)\(l\)\(\max\)

容易发现 \(L\) 是单增的,考虑先把询问区间拆分为 \(\log\) 个满的区间,然后会发现 \(l\) 会覆盖当前区间的一个前缀,其后缀仍是 \(L\)

如果这个分界线在左边,则我们希望处理好右边的答案,并递归左边。注意到,对于右边的 \(L\) 的限制,它只受当前区间的影响,具体而言,右边的答案是 \(\max_{i\in[mid+1,r]}(S_i-S_{\max_{j\in[l,i]}lst[j]})\)。因为左边已经受到 \(l\) 限制,而在当前区间内到了一个位置,\(L\)\(>l\) 了,因此一定存在一个当前线段树区间内的 \(lst\) 是真正有贡献的,只需考虑当前区间内的 \(lst\)\(L\) 的贡献就行了。

如果分界线在右边,则左边由于 \(S\) 也是单增的,左边的最大值是 \(S_{mid}-S_v\),递归右边,但是要给 \(l\) 加一个左边这部分的限制,即变为 \(\max(l,\max_{i\in[l,mid]}lst_i)\)

判断分界线在左边还是右边只需要维护区间 \(\max lst\) 即可。将当前区间右区间的答案放在当前区间存储,即 pushup 的时候,使 tr[p].ans = _Query(tr[ls].Lmax, m + 1, t, rs) 即可(注意右边的答案是受当前区间左半部分的影响的(它的 \(L\) 对应的 \(lst\) 的贡献范围是当前整个区间),因此传入 tr[ls].Lmax)。

#include <bits/stdc++.h>
using namespace std;
void File(string s) { freopen((s + ".in").c_str(), "r", stdin), freopen((s + ".out").c_str(), "w", stdout); }
const int N = 200005;
int n, m, q, co[N], lst[N];
long long pre[N];
set<int> po[N];
struct Segment_tree {
	#define ls (p << 1)
	#define rs (p << 1 | 1)
	struct Node {
		long long Lmax, Lmin, ans;
		Node (long long _lmax = 0, long long _lmin = 0, long long _ans = 0) { Lmax = _lmax, Lmin = _lmin, ans = _ans; }
	} tr[N << 2];
	void Pushup(int s, int t, int p) {
		tr[p].Lmax = max(tr[ls].Lmax, tr[rs].Lmax);
		tr[p].Lmin = min(tr[ls].Lmin, tr[rs].Lmin);
		int m = (s + t) >> 1;
		tr[p].ans = _Query(tr[ls].Lmax, m + 1, t, rs);
	}
	void Build(int s, int t, int p) {
		if (s == t) {
			tr[p] = { lst[s], lst[s], pre[s] - pre[lst[s]] };
			return ;
		}
		int m = (s + t) >> 1;
		Build(s, m, ls);
		Build(m + 1, t, rs);
		Pushup(s, t, p);
	}
	void Modify(int x, int s, int t, int p) {
		if (s == t) {
			tr[p] = { lst[x], lst[x], pre[s] - pre[lst[x]] };
			return ;
		}
		int m = (s + t) >> 1;
		if (x <= m) {
			Modify(x, s, m, ls);
		}
		else {
			Modify(x, m + 1, t, rs);
		}
		Pushup(s, t, p);
	}
	long long _Query(int v, int s, int t, int p) {
		if (s == t) {
			return pre[s] - pre[max(lst[s], v)];
		}
		int m = (s + t) >> 1;
		if (tr[ls].Lmax < v) {
			return max(pre[m] - pre[v], _Query(v, m + 1, t, rs));
		}
		else {
			return max(_Query(v, s, m, ls), tr[p].ans);
		}
	}
	long long Query(int l, int r, int v, int s, int t, int p) {
		if (l <= s && r >= t) {
			return _Query(v, s, t, p);
		}
		int m = (s + t) >> 1;
		long long ans = 0;
		if (l <= m) {
			ans = max(ans, Query(l, r, v, s, m, ls));
		}
		if (r > m) {
			ans = max(ans, Query(l, r, max((long long)v, tr[ls].Lmax), m + 1, t, rs));
		}
		return ans;
	}
} seg;
signed main() {
	File("norepeat");
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", pre + i);
		pre[i] += pre[i - 1];
	}
	for (int i = 1; i <= m; i++) {
		po[i].insert(0);
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", co + i);
		lst[i] = *po[co[i]].rbegin();
		po[co[i]].insert(i);
	}
	seg.Build(1, n, 1);
	while (q--) {
		int opt;
		scanf("%d", &opt);
		if (opt == 1) {
			int l, r;
			scanf("%d%d", &l, &r);
			printf("%lld\n", seg.Query(l, r, l - 1, 1, n, 1));
		}
		else {
			int p, w;
			scanf("%d%d", &p, &w);
			auto it = next(po[co[p]].lower_bound(p));
			po[co[p]].erase(p);
			if (it != po[co[p]].end()) {
				lst[*it] = *prev(it);
				seg.Modify(*it, 1, n, 1);
			}
			it = po[w].lower_bound(p);
			if (it != po[w].end()) {
				lst[*it] = p;
				seg.Modify(*it, 1, n, 1);
			}
			it = po[w].insert(p).first;
			lst[p] = *prev(it);
			co[p] = w;
			seg.Modify(p, 1, n, 1);
		}
	}
	return 0;
}
posted @ 2025-08-07 18:16  Air_CoIor5  阅读(22)  评论(0)    收藏  举报