题解:AT_jsc2019_final_h Distinct Integers

节选自:线段树进阶应用学习笔记(四):单侧递归问题

题目链接:AT_jsc2019_final_h Distinct Integers

第一届日本最强程序员学生锦标赛决赛

首先,这道题目要求的是区间有多少个子区间没有重复数字,如果我们记录一下每个数字 i 前一次出现的位置 prei,那么问题就变成了有多少个子区间包括 i 但不包括 prei,由于区间内每个数字都不能重复出现,于是固定住 rl 最左就只能到 maxi=Lrprei+1,此时对于答案的贡献就是 rl+1,于是答案就变成了 i=LR(imaxj=Liprej)=(L+R)(RL+1)2i=LRmaxj=Liprej,于是问题就变成了求区间 [l,r] 中前缀 pre 的最大值的和。

现在我们将问题转化成了区间前缀最大值问题,依然考虑使用线段树单侧递归的写法。考虑 id 的两个儿子以及 id 的右儿子的两个儿子 lsrs,如果 id 的左儿子的区间最大值比 id 的右儿子的大,那么 id 的右儿子的所有前缀最大值都无法对答案造成贡献,于是 id 的答案就是 id 的左儿子的答案加上 id 的右儿子的长度乘以 id 的左儿子的区间最大值。

考虑 ls 的区间最大值小于 id 的左儿子的这种情况,此时 ls 的所有前缀最大值都无法保留,于是答案加上 ls 的长度乘上 id 的左儿子的区间最大值,往 rs 递归即可。

最后考虑 ls 的区间最大值大于 id 的左儿子的这种情况,由于如果 rs 的一个前缀最大值大于 ls 的区间最大值,那么一定会在 id 的前缀最大值中被保留下来,而且 id 的左儿子的答案等于 ls 的答案加上在 ls 遮挡下 rs 的答案,因此将答案加上 t[id << 1 | 1].ans - t[ls].ans,往 ls 递归即可。

修改操作可以给每种值开一个集合,记录这种值出现的下标。将 ax 修改为 y 时,只用将 xax 集合中的后继的 pre 修改为 x 的前驱,并将 xpre 修改为它在 y 集合中的前驱,将 x 后继的 pre 修改为 x 即可。

注意一下,如果最大的 prei 都小于 L,那么 l 只能取到 L,不能更小,因此需要特判一下。那么这道题就在 O(nlog2n) 的时间复杂度内解决。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 9;
struct Node{
	int ans, maxn;
} t[N << 2];
int a[N], pre[N], n, q;
int prequery(int id, int l, int r, int ql, int qr){
	if(ql <= l && r <= qr)
		return t[id].maxn;
	int mid = (l + r) >> 1, ans = 0;
	if(ql <= mid)
		ans = prequery(id << 1, l, mid, ql, qr);
	if(qr > mid)
		ans = max(ans, prequery(id << 1 | 1, mid + 1, r, ql, qr));
	return ans;
}
int pushup(int now, int l, int r, int cmp){
	int res = 0;
	while(true){
		if(l == r){
			res += max(t[now].maxn, cmp);
			break;
		}
		int mid = (l + r) >> 1;
		if(cmp > t[now << 1].maxn){
			res += (mid - l + 1) * cmp;
			now = now << 1 | 1;
			l = mid + 1;
		} else {
			res += t[now].ans - t[now << 1].ans;
			now = now << 1; 
			r = mid;
		}
	}
	return res;
}
void build(int id, int l, int r){
	if(l == r){
		t[id].maxn = t[id].ans = pre[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(id << 1, l, mid);
	build(id << 1 | 1, mid + 1, r);
	t[id].maxn = max(t[id << 1].maxn, t[id << 1 | 1].maxn);
	t[id].ans = t[id << 1].ans + pushup(id << 1 | 1, mid + 1, r, t[id << 1].maxn);
}
void modify(int id, int l, int r, int q, int qx){
	if(l == r){
		t[id].ans = t[id].maxn = qx;
		return;
	}
	int mid = (l + r) >> 1;
	if(q <= mid)
		modify(id << 1, l, mid, q, qx);
	else
		modify(id << 1 | 1, mid + 1, r, q, qx);
	t[id].maxn = max(t[id << 1].maxn, t[id << 1 | 1].maxn);
	t[id].ans = t[id << 1].ans + pushup(id << 1 | 1, mid + 1, r, t[id << 1].maxn);
}
int query(int id, int l, int r, int ql, int qr){
	if(ql <= l && r <= qr)
		return pushup(id, l, r, l - 1 ? prequery(1, 1, n, 1, l - 1) : 0);
	int mid = (l + r) >> 1, ans = 0;
	if(ql <= mid)
		ans += query(id << 1, l, mid, ql, qr);
	if(qr > mid)
		ans += query(id << 1 | 1, mid + 1, r, ql, qr);
	return ans;
}
vector <int> s[N];
signed main(){
	scanf("%lld%lld", &n, &q);
	for(int i = 1; i <= n; i++)
		s[i].push_back(0);
	for(int i = 1; i <= n; i++){
		scanf("%lld", &a[i]);
		a[i]++;
		s[a[i]].insert(lower_bound(s[a[i]].begin(), s[a[i]].end(), i), i);
		pre[i] = *--lower_bound(s[a[i]].begin(), s[a[i]].end(), i);
	}
	build(1, 1, n);
	while(q--){
		int opt, x, y;
		scanf("%lld%lld%lld", &opt, &x, &y);
		x += 1;
		y += !opt;
		if(opt == 0){
			if(upper_bound(s[a[x]].begin(), s[a[x]].end(), x) != s[a[x]].end()){
				modify(1, 1, n, *upper_bound(s[a[x]].begin(), s[a[x]].end(), x), *--lower_bound(s[a[x]].begin(), s[a[x]].end(), x));
				pre[*upper_bound(s[a[x]].begin(), s[a[x]].end(), x)] = *--lower_bound(s[a[x]].begin(), s[a[x]].end(), x);
			}
			s[a[x]].erase(lower_bound(s[a[x]].begin(), s[a[x]].end(), x));
			s[y].insert(lower_bound(s[y].begin(), s[y].end(), x), x);
			pre[x] = *--lower_bound(s[y].begin(), s[y].end(), x);
			modify(1, 1, n, x, *--lower_bound(s[y].begin(), s[y].end(), x));
			if(upper_bound(s[y].begin(), s[y].end(), x) != s[y].end()){
				modify(1, 1, n, *upper_bound(s[y].begin(), s[y].end(), x), x);
				pre[*upper_bound(s[y].begin(), s[y].end(), x)] = x;
			}	
			a[x] = y;
		} else {
			modify(1, 1, n, x, x - 1);
			printf("%lld\n", (x + y) * (y - x + 1) / 2 - query(1, 1, n, x, y));
			modify(1, 1, n, x, pre[x]);
		}
	}
	return 0;
}
posted @   JPGOJCZX  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示