解题思路
由题目可以看出,暴力解法一定会超时。 当我们寻找当前输入数之前的最大值,要跳过冗余的数,这一点可以通过并查集实现 其中合并操作需要做出改动
合并操作要让输入数的 父节点 和 父节点+1
进行合并,取较大的做父节点,即祖先节点一定为最大数,配合这个合并操作,查询时可以使用路径压缩(经过测试,不使用路径压缩某些测评点会超时,道理跟暴力解一样)
同时,使用vis数组进行标记,注意前后的连续性,如果前后连续,将前一个节点的父节点改为后一个节点
完整代码
#include <iostream>
using namespace std;
const int MAXN = 1e5 + 10;
const int MAXA = 1e6 + MAXN;
//并查集变种
int n, x;
//a数组记录对应数的父节点,vis数组记录是否访问过
int a[MAXA], vis[MAXA];
//初始化
void init(int n)
{
for(int i = 1; i <= n; i++)
{
a[i] = i;
}
}
//查询(路径压缩)
int find(int x)
{
if(a[x] == x)
return x;
return a[x] = find(a[x]);
}
//合并(本题需要改变的地方)
int merge(int x)
{
//查询输入的数是否已经出现过,若出现过,则访问它的父节点并+1;未出现过则其为自身的父节点
int res = vis[x] ? find(x) + 1 : x;
//最后用vis数组标记
vis[res] = 1;
//返回结果
return res;
}
int main()
{
cin >> n;
init(n);
for(int i = 1; i <= n; i++)
{
cin >> x;
int res = merge(x);
cout << res << ' ';
//下面一步十分重要!!!
//判断前后是否连续,保证merge时的find(x)+1步骤可以使得res取得符合题意的值
//当res-1已经访问过时,将其父节点改为res
if(res != 1 && vis[res - 1])
a[res - 1] = res;
//当res+1已经访问过时,将res的父节点改为res+1
if(vis[res + 1])
a[res] = res + 1;
}
return 0;
}