给定一个 数组补全(秋季每日一题 10)_i++ 的排列 数组补全(秋季每日一题 10)_构造_02

已知,对于 数组补全(秋季每日一题 10)_环图_03数组补全(秋季每日一题 10)_构造_04

现在,因为一些原因,数组中的部分元素丢失了。

请你将数组丢失的部分补全,要求数组在补全后仍然是一个 数组补全(秋季每日一题 10)_i++ 的排列,并且对于 数组补全(秋季每日一题 10)_环图_03数组补全(秋季每日一题 10)_构造_04

输入格式
第一行包含整数 数组补全(秋季每日一题 10)_i++_08,表示共有 数组补全(秋季每日一题 10)_i++_08

每组数据第一行包含一个整数 数组补全(秋季每日一题 10)_补全_10

第二行包含 数组补全(秋季每日一题 10)_补全_10 个整数 数组补全(秋季每日一题 10)_构造_02。如果 数组补全(秋季每日一题 10)_补全_13,则表示 数组补全(秋季每日一题 10)_i++_14

输出格式
每组数据一行,输出补全后的 数组补全(秋季每日一题 10)_补全_15

如果方案不唯一,则输出任意合理方案即可。

数据范围
数组补全(秋季每日一题 10)_数组_16
数组补全(秋季每日一题 10)_i++_17
数组补全(秋季每日一题 10)_补全_18至少两个 数组补全(秋季每日一题 10)_i++_14数组补全(秋季每日一题 10)_环图_20
同一测试点内所有 数组补全(秋季每日一题 10)_补全_10 的和不超过 数组补全(秋季每日一题 10)_补全_22
数据保证有解。

输入样例:

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

输出样例:

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

  1. 记录孤立点
  2. 将孤立点插入任意一个开环中
  3. 将开环闭合
  4. 如何没有一个开环,则将孤立点集自成环
#include<iostream>
#include<cstring>
#include<vector>

using namespace std;

const int N = 200010;

int n;
int pre[N], ne[N];
bool st[N]; // 标记孤立点

int main(){

int t;
scanf("%d", &t);

while(t--){

scanf("%d", &n);
memset(ne, 0, 4*(n+1));
memset(pre, 0, 4*(n+1));
memset(st, 0, n+1); // 孤立点

for(int i = 1; i <= n; i++) scanf("%d", &ne[i]), pre[ne[i]] = i;

vector<int> v;
for(int i = 1; i <= n; i++)
if(!ne[i] && !pre[i]){
v.push_back(i);
st[i] = true;
}

bool flag = false;
for(int i = 1; i <= n; i++){
if(!st[i] && ne[i] && !pre[i]){ // 开环的头节点

int u = ne[i]; // 尾
while(ne[u]) u = ne[u];

if(!flag){
for(int j = 0; j < v.size(); j++){

ne[u] = v[j];
u = ne[u];
}
flag = true;
}

ne[u] = i; // 连成环
}
}

int hh = 0, tt = 0;
if(!flag){ // 如过孤立点没有被插入任何一个开环,则他们自己成环

for(int i = 0; i < v.size(); i++)
if(!hh && !tt) hh = tt = v[i];
else ne[tt] = v[i], tt = v[i];

ne[tt] = hh;
}

for(int i = 1; i <= n; i++) printf("%d ", ne[i]);
puts("");
}

return 0;
}