题目大意
有
n
n
n个人,每个人都要给别人送一份礼物,而且在每个人的心目中都有一个想送礼物的人,记为
b
i
b_i
bi。
为了公平,应该保证每一个人都收到且只收到一份礼物。
问如何安排每个人将给谁送礼物,使得更多的人能够将礼物送给题目想送的人。
多种方案则任意输出一组即可。
时间限制
2s
数据范围
n ≤ 2 × 1 0 5 n\le2\times10^5 n≤2×105
题解
不难看出,每个人收礼物和送礼物是两个独立事件,因此可以将每个人分为两个点,一个代表送礼物的,一个收礼物的。
这样就可以转化成了一个二分图,每个送礼物的点连出去
n
−
1
n-1
n−1条边,每个收礼物的点连进来
n
−
1
n-1
n−1条边。
根据二分图判定定理t-条件,可知一定存在完全匹配。
一种贪心的思想,先将每个人尽可能匹配到他们想送礼物的人,对于剩下不能被匹配上的人,再用一个普通的二分图匹配。
由于知道一定存在解,因此可以省去不少特批。
Code
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <queue>
#include <map>
#define G getchar
#define ll long long
using namespace std;
int read()
{
char ch;
for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
int n = 0 , w;
if (ch == '-')
{
w = -1;
ch = G();
} else w = 1;
for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
return n * w;
}
const int N = 200005;
int n , m , a[N] , f[N] , w[N] , top , g[N] , ans;
int to[N];
bool bz[N];
int main()
{
//freopen("c.in","r",stdin);
//freopen("l.out","w",stdout);
for (int T = read() ; T ; T--)
{
memset(bz , 1 , sizeof(bz));
n = read();
ans = 0;
for (int i = 1 ; i <= n ; i ++)
{
a[i] = read();
f[i] = 0;
if (bz[a[i]])
{
ans++;
bz[a[i]] = 0;
f[i] = a[i];
to[a[i]] = i;
}
}
m = top = 0;
for (int i = 1 ; i <= n ; i++)
{
if (f[i] == 0)
{
m++;
g[m] = i;
}
if (bz[i])
{
top ++;
w[top] = i;
}
}
if (m == 1)
{
if (w[1] == g[1])
{
int x = to[a[w[1]]];
f[g[1]] = f[x];
f[x] = w[1];
}
else f[g[1]] = w[1];
printf("%d\n", ans);
for (int i = 1 ; i <= n ; i++)
printf("%d ", f[i]);
puts("");
continue;
}
for (int i = 1 ; i < m ; i++)
if (g[i] == w[i]) swap(w[i] , w[i + 1]);
if (g[m] == w[m]) swap(w[1] , w[m]);
for (int i = 1 ; i <= m ; i++)
f[g[i]] = w[i];
printf("%d\n", ans);
for (int i = 1 ; i <= n ; i++)
printf("%d ", f[i]);
puts("");
}
return 0;
}