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;
}