D. Secret Santa
题意:
给t组样例
每组样例给n个数
a[1] , a[2] , a[3] ...... a[n]
(t组样例n的总和<=2e5,a[i] <= n)
并且保证a[i] != i
求一个数组p
并且这个数组p为1到n的全排列中的一种方式
求 p[i] == a[i] 的个数最大并且p[i] != i
输出这个个数的最大值和p数组
思路:
首先先把可以匹配的匹配了
举个例子
a 6 4 6 2 4 5 1
从头到尾扫一遍
p 6 4 _ 2 _ 5 1
还差3 7没填
然后在考虑怎么安排3 7的位置 使得p[i] != i
很明显应该
p 6 4 7 2 3 5 1
也就是说题目转换成为了
怎么安排未匹配的数
使得p[i] != i
这里有个性质
自闭了1h才想出来
假设现在没有匹配的数是 1 2 3 4 5 6 7
有7个位置要填
_ _ _ _ _ _ _
那我1 2 3 4 5 6 7 正序填
7个都冲突出现了p[i] = i 的情况
如果倒叙填
是 7 6 5 4 3 2 1
只有p[4] 冲突了
那是不是说明了正序填和倒叙填有一种情况
最多只冲突一次
(可以自己多找几组样例看看是不是对的)
如果冲突一次
找到这个数交换一下即可
时间复杂度:O n
#include<bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
#define pll pair<int,int>
#define x first
#define y second
#define sf(x) scanf("%d",&x)
#define sfl(x) scanf("%lld",&x)
typedef long long ll ;
using namespace std;
const int N = 2e5 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int t ;
int n ;
int a[N] ; // a数组
bool st[N] ; // 判断这个数有没有出现过
int ans[N] ;
int s[N] ; // s , ans最后的答案数组
int main()
{
cin >> t ;
while(t--)
{
cin >> n ;
fer(i,1,n) sf(a[i]) ;
fer(i,1,n) st[i] = ans[i] = 0 ; // 初始化
int cnt = 0 ;
fer(i,1,n) // 把可以匹配的先匹配了
{
if(!st[a[i]])
{
cnt ++ ;
st[a[i]] = 1 ;
ans[i] = a[i] ;
}
}
vector<int> q ; // 找到未匹配的数
fer(i,1,n)
{
if(!st[i])
q.push_back(i) ;
}
//fer(i,1,n) cout << a[i] << " " ;
sort(q.begin(),q.end()) ;
reverse(q.begin(),q.end()) ;
// 倒叙填一遍
int cnt1 = 0 , cnt2 = 0 ;
// cnt1为倒叙的冲突个数 , cnt2反之
fer(i,1,n)
{
s[i] = ans[i] ;
}
// 先让s数组和ans数组相等
// 倒叙填ans数组
int k = 0 ;
fer(i,1,n)
{
if(!ans[i])
{
ans[i] = q[k ++] ;
}
}
fer(i,1,n)
{
if(ans[i] == i) cnt1 ++ ;
}
// 正序填s数组
reverse(q.begin(),q.end()) ;
k = 0 ;
fer(i,1,n)
{
if(!s[i])
{
s[i] = q[k ++] ;
}
}
fer(i,1,n)
{
if(s[i] == i) cnt2 ++ ;
}
if(cnt1 > cnt2)
{
// 如果ans数组冲突值大了,把s数组给ans
fer(i,1,n)
{
ans[i] = s[i] ;
}
}
// 找到这个冲突值的下标
int xia = 0 ;
fer(i,1,n)
{
if(ans[i] == i)
{
xia = i ;
break ;
}
}
// 然后交换
fer(i,1,n)
{
if(ans[i] == a[xia] && i != xia)
{
swap(ans[i],ans[xia]) ;
break ;
}
}
// 最后输出答案
cout << cnt << "\n" ;
fer(i,1,n) cout << ans[i] << " " ;
cout << "\n" ;
}
return 0;
}