修改数组 平衡树 维护区间 经典题

给定一个长度为 N 的数组 A=[A1,A2,⋅⋅⋅AN],数组中有可能有重复出现的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。
小明会依次修改 A2,A3,⋅⋅⋅,AN。
当修改 Ai 时,小明会检查 Ai 是否在 A1∼Ai−1 中出现过。
如果出现过,则小明会给 Ai 加上 1;如果新的 Ai 仍在之前出现过,小明会持续给 Ai 加 1,直到 Ai 没有在 A1∼Ai−1 中出现过。
当 AN 也经过上述修改之后,显然 A 数组中就没有重复的整数了。
现在给定初始的 A 数组,请你计算出最终的 A 数组。
输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,⋅⋅⋅,AN。
输出格式
输出 N 个整数,依次是最终的 A1,A2,⋅⋅⋅,AN。
数据范围
1≤N≤105,
1≤Ai≤106
输入样例:
5
2 1 1 3 4
输出样例:
2 1 3 4 5

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#include<climits>

using namespace std;

int n;

typedef pair<int, int> PII;
set<PII> sgs;


int main() {
	int x;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &x);
		if (i == 0) {
			sgs.insert({ x,x });
			printf("%d ", x);
			continue;
		}
		//在集合中去找一个区间包含x的,本来我们只需要找一个数x,但是由于set里面存的区间,一个数和一个区间肯定无法直接比较大小,所以我们要把这个数x变成一个区间,即<x,INT_MIN>,就可以用lower_bound查找了,找到的是第一个大于或等于这个pair的pair(pair先比较第一个关键字的大小。如果相等,再比较第二个关键字的大小)。

		//二分查找包含x的区间
		auto it = sgs.lower_bound(PII(x, INT_MIN));
		//如果包含 即[*,?] (?>=x)
		if (it != sgs.end() && it->second <= x) {
			//printf("包含\n");
			//修改x为这个区间的右端点+1,即让x脱离这个区间。
			x = it->first + 1;
			//合并这两个区间,且必然能合并
			int l = it->second;//备份区间左值
			sgs.erase(it);//删除原来的区间
			sgs.insert(PII(x, l));//插入新的区间
			//我们去找一个区间的右端点大于等于x+1
			//即[3,x]->[?,?] (?>=x+1)
			it = sgs.lower_bound({ x,l });
			auto back = sgs.lower_bound(PII(x+1, INT_MIN));
			if (back != sgs.end() && back->second == x + 1) {
				//如果找到了这样的一个pair(右端点大于x,左端点等于x+1)
				//那么这两个区间又可以合并了
				int r = back->first;
				int l = it->second;
				sgs.erase(it);
				sgs.erase(back);
				sgs.insert(PII(r, l));
			}
		}
		else {
			//没有一个区间包含x,那么x就不需要修改
			//先插入到set里面
			sgs.insert(PII(x, x));
			//检查是否可以合并 [?,x-1],[x,x],[x+1,?]
			//找右端点大于等于x-1的
			auto it = sgs.lower_bound(PII(x, x));//先定位
			auto pre = it;
			if (pre != sgs.begin()) {
				pre--;
				if (pre->first + 1 == it->second) {
					//合并
					int l = pre->second;
					int r = it->first;
					sgs.erase(pre);
					sgs.erase(it);
					sgs.insert({ r,l });
				}
			}

			//定位[?,x]
			it = sgs.lower_bound(PII(x, INT_MIN));
			auto back = sgs.lower_bound({ x + 1,INT_MIN });
			if (back != sgs.end() && back->second == it->first + 1) {
				//合并
				int l = it->second;
				int r = back->first;
				sgs.erase(it);
				sgs.erase(back);
				sgs.insert(PII(r, l));
			}
		}
		printf("%d ", x);
	}
	printf("\n");
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值