订单编号 STD set维护区间一附并查集巨妙方法

题目链接
在这里插入图片描述

题目大意

给出一个数组a,对于数组中的每个数,找出大于等于这个数,且在这个数前边没有出现过的最小的数字;

例如 a = [ 2 , 3 , 4 , 1 , 1 , 1 ] a = [2, 3, 4, 1, 1, 1] a=[2,3,4,1,1,1], 那么答案 a = [ 2 , 3 , 4 , 1 , 5 , 6 ] a = [2, 3 ,4 ,1, 5, 6] a=[2,3,4,1,5,6]

题目思路

用set维护区间,set中区间的存放方式为 [ r , l ] [r,l] [r,l],这里我们给出具体的维护过程

a = [ 2 , 3 , 4 , 1 , 1 , 1 ] a = [2, 3, 4, 1, 1, 1] a=[2,3,4,1,1,1]

  • i = 1 , s e t = [ 2 , 2 ] i = 1,set = {[2, 2]} i=1,set=[2,2]

  • i = 2 , s e t = [ 2 , 2 ] , [ 3 , 3 ] i = 2,set = {[2, 2], [3, 3]} i=2,set=[2,2],[3,3]

  • i = 3 , s e t = [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 4 ] i = 3,set = {[2, 2],[3,3],[4,4]} i=3,set=[2,2],[3,3],[4,4]

  • i = 4 , s e t = [ 1 , 1 ] [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 4 ] i = 4,set = {[1,1][2, 2],[3,3],[4,4]} i=4,set=[1,1][2,2],[3,3],[4,4]

  • i = 5 , s e t = [ 2 , 1 ] [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 4 ] i = 5,set = {[2,1][2, 2],[3,3],[4,4]} i=5,set=[2,1][2,2],[3,3],[4,4]

    • s e t = [ 3 , 1 ] , [ 3 , 3 ] , [ 4 , 4 ] set = {[3,1],[3,3],[4,4]} set=[3,1],[3,3],[4,4]
    • s e t = [ 4 , 1 ] , [ 4 , 4 ] set = {[4,1],[4,4]} set=[4,1],[4,4]
    • s e t = [ 5 , 1 ] set = {[5,1]} set=[5,1]
  • i = 6 , s e t = [ 5 , 1 ] , [ 1 , 1 ] i = 6,set = {[5,1],[1,1]} i=6,set=[5,1],[1,1]

    • s e t = [ 6 , 1 ] set = {[6,1]} set=[6,1]

所以每个数的最终答案为最后合并完的区间后右区间的值

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int n;
	cin >> n;
	vector<int> a(n + 1, 0);
	set<pair<int, int> > st;
	for(int i = 1;i <= n;i++) {
		cin >> a[i];
		auto p = pair<int, int>(a[i], a[i]);
		auto iter = st.lower_bound(pair<int, int>(a[i], 0));
		if(iter == st.end()) {
			st.insert(p);
		}
		else {
			if(a[i] < (*iter).second) {
				st.insert(p);
			}
			else {
				while(next(iter) != st.end()) {
					if((*next(iter)).second == (*iter).first + 1) {
						auto it1 = next(iter), it2 = iter;
						pair<int, int> p1 = pair<int, int>((*it1).first, (*it2).second);
						st.erase(it1);
						st.erase(it2);
						st.insert(p1);
						iter = st.lower_bound(p1);
					}
					else {
						break;
					}
				}
				a[i] = (*iter).first + 1;
				auto it = *iter;
				it.first += 1;
				st.erase(iter);
				st.insert(it);
				iter = st.lower_bound(it);
			}
		}
	}
	for(int i = 1;i <= n;i++) cout << a[i] << ' ';cout << '\n';

    return 0;
}

更女少的做法!

我们可以使用并查集来维护一段连续的数字,当当前数字被使用后,我们可以将当前值和下一个值进行合并,并且令这个连通块的祖先为这个联通块最大的数(同时也是当前的答案)

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

unordered_map<int, int> fa;

int find(int x) {
	if(fa[x] == 0) fa[x] = x;
	return fa[x] == x? x : fa[x] = find(fa[x]);
}

void merge(int x, int y) {
	x = find(x), y = find(y);
	if(x == y) return;
	if(x > y) swap(x, y);
	fa[x] = y;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int n;
	cin >> n;
	vector<int> a(n + 1);
	for(int i = 1;i <= n;i++) {
		cin >> a[i];
		a[i] = find(a[i]);
		merge(a[i], a[i] + 1);
	}
	for(int i = 1;i <= n;i++) cout << a[i] << ' ';cout << '\n';

    return 0;
}

不摆了,卷!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值