2023/10/11 #D 莫队

类似题目:楼房重建。

受教了。
考虑维护后驱,那么往右延伸的长度就是后缀 min。
那么区间对后缀 min 求和再减去等差数列即可!
考虑后缀的高贵性质再做线段树。
记录 s[l,r,c] 表示对于区间做 min(c,sufminx) 这个操作的和。
对于这一类问题可以考虑兔队线段树,大致上是在做 pushup 时这样对左儿子做一遍 s[l,mid,minrc]
那么怎么做呢?
考虑线段树上二分,必然有一个点靠后的 lim 这个限制。一段贡献是区间推平,一段贡献是儿子的值!!
真的好厉害,深深震撼到了。
查询的时候我们考虑对于查询到了的每个 x 去求 s[lx,rx,qv] 即可。
这个题还要维护一个 set。单点修改即可。
注意到后缀 min 的限制,我们要在 query 时先查询右儿子,然后对于每一个 mnxmin
感觉现在理解很到位。

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; i ++)
#define per(i, r, l) for (int i = r; i >= l; i --)
#define lc x << 1
#define rc x << 1 | 1

/*王源他不发龙狙证明他没有素质*/
using namespace std;
typedef long long ll;

const int _ = 3e5 + 5;

int n, m;
int a[_], suf[_];
set <int> s[_];
ll ans[_ << 2], mn[_ << 2], res, li;

ll calc (int x, int l, int r, ll lim) {
	if (l == r) return min(ans[x], lim);
	int mid = (l + r) >> 1;
	if (mn[rc] <= lim) return ans[x] - ans[rc] + calc(rc, mid + 1, r, lim);
	return calc(lc, l, mid, lim) + 1ll * lim * (r - mid);
}
void pushup (int x, int l, int mid, int r) {
	mn[x] = min(mn[lc], mn[rc]);
	ans[x] = calc(lc, l, mid, mn[rc]) + ans[rc];
}
void build (int x, int l, int r) {
	if (l == r) return ans[x] = mn[x] = suf[l], void();
	int mid = (l + r) >> 1;
	build(lc, l, mid), build(rc, mid + 1, r);
	pushup(x, l, mid, r);
}
void insert (int x, int l, int r, int v, ll k) {
	if (l == r) return ans[x] = mn[x] = k, void();
	int mid = (l + r) >> 1;
	v <= mid ? insert(lc, l, mid, v, k) : insert(rc, mid + 1, r, v, k);
	pushup(x, l, mid, r);
}
void query (int x, int l, int r, int ql, int qr) {
	if (ql <= l && r <= qr) {
		res += calc(x, l, r, li),
		li = min(li, mn[x]);
		return ;  
	}
	int mid = (l + r) >> 1;
	if (qr > mid) query(rc, mid + 1, r, ql, qr);
	if (ql <= mid) query(lc, l, mid, ql, qr);
}
int main () {
	/*
	  freopen(".in", "r", stdin);
	  freopen(".out", "w", stdout);
	 */
	cin >> n >> m;
	rep(i, 1, n) scanf("%d", & a[i]), s[i].insert(n + 1);
	per(i, n, 1) suf[i] = *(s[a[i]].begin()), s[a[i]].insert(i);
	build(1, 1, n);
	while(m --) {
		int op, x, y;
		scanf("%d%d%d", & op, & x, & y);
		if (op == 1) {
			auto it = s[a[x]].find(x);
			int nxt = *(++ it); --it;
			if (it != s[a[x]].begin()) { --it; insert(1, 1, n, *it, nxt); }
			s[a[x]].erase(x), it = s[y].lower_bound(x), a[x] = y;
			insert(1, 1, n, x, *it);
			if (it != s[y].begin()) --it, insert(1, 1, n, *it, x);
			s[y].insert(x);
		} else {
			res = 0, li = y + 1;
			query(1, 1, n, x, y);
			res -= 1ll * (y + x) * (y - x + 1) / 2;
			printf("%lld\n", res);
		}
	}
	return 0;
}
/*
4 3
1 1 2 1
2 1 3
1 2 3
2 1 4  
 */
posted @   Cust10  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示