E. Restoring the Permutation

E. Restoring the Permutation

在这里插入图片描述
在这里插入图片描述

Example

input

4
7
3 3 4 4 7 7 7
4
1 2 3 4
7
3 4 5 5 5 7 7
1
1

output

3 1 4 2 7 5 6 
3 2 4 1 7 6 5 
1 2 3 4 
1 2 3 4 
3 4 5 1 2 7 6 
3 4 5 2 1 7 6 
1 
1 

题目大意:

给一个非严格升序的数组,替换第一次出现后后面再出现的重复数字,使得1-n的每个数字只出现一次,分别求字典序最大和最小的序列。

思路:

字典序较小的比较好求:统计出现重复数字的空,用1-n没出现过的数字,从小到大填进去。

求字典序较大的序列:统计出现重复数字的空,填进去比当前数字小的最大的数,要是用朴素法枚举,复杂度就可以卡到N*N,所以得想办法把复杂度降到N,用set的lower_bound可以轻易做到,差不多是N * logN。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#define int long long
#define endl '\n'
using namespace std;
const int N=2e5+7;

int a[N];
int b[N];
bool check[N];
bool check2[N];
set<int>s;

signed main()
{
    
    
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;
	cin>>t;
	while(t--)
	{
    
    
		int n;
		int k=1;
		cin>>n;
		for(int i=1;i<=n;++i)s.insert(i);//先都装进去
		for(int i=1;i<=n;++i)
		{
    
    
			cin>>a[i];
			b[i]=a[i];
			if(check[a[i]])
			{
    
    
				a[i]=k;
				check[k]=1;
			}
			else
			{
    
    
				check[a[i]]=1;
			}
			while(check[k])++k;
			if(s.find(b[i])!=s.end())
				s.erase(b[i]);//出现的就不能用来填了,所以删掉
		}
		for(int i=1;i<=n;++i)
		{
    
    
			if(check2[b[i]])
			{
    
    
				b[i]=*prev(s.lower_bound(b[i]));//lower求第一个大于等于b[i]的数,它的前一个就是比b[i]小的最大的数
				s.erase(b[i]);
			}
			else
			{
    
    
				check2[b[i]]=1;
			}
		}
		for(int i=1;i<=n;++i)
		{
    
    
			cout<<a[i]<<' ';
			a[i]=0;
		}
		cout<<endl;
		for(int i=1;i<=n;++i)
		{
    
    
			cout<<b[i]<<' ';
			b[i]=0;
			check[i]=0;//顺便初始化
			check2[i]=0;
		}
		cout<<endl;
		s.clear();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Story_1419/article/details/116989627