传送门:QAQ
题意:给你n个点的图,每个点都有一个父亲,然后让你改变尽量少的点的父亲,让它变成一颗树。输出改变的次数和最后的连接情况。
思路:首先我们要把成环的图拆开,然后给每个链都找一个父亲,然后就是选根节点了(如果已经有根节点了,就随便找一个已有的根节点,如果没有,就随便找一个链的根节点)。
代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
int ax[200100];
vector<int>vt;
int father[200100];
int n;
int find(int x) {
if (x != father[x]) return father[x] = find(father[x]);
else return x;
}
void unio(int a, int b) {
int x1 = find(a);
int y1 = find(b);
if (x1 != y1) {
father[x1] = y1;
}
}
void init() {
for (int i = 0; i <= n; i++) {
father[i] = i;
}
}
int main(void) {
scanf("%d", &n);
init();
for (int i = 1; i <= n; i++) {
scanf("%d", &ax[i]);
if (find(i) == find(ax[i]) && i != ax[i]) {
ax[find(i)] = -1;
}
else {
unio(i, ax[i]);
}
}
int tt = -1;
int sum = 0;
for (int i = 1; i <= n; i++) {
if (ax[i] == i) {
tt = i;
break;
}
}
for (int i = 1; i <= n; i++) {
if (ax[i] == -1) {
if (tt == -1) tt = i;
ax[i] = tt;
sum++;
unio(i, tt);
}
}
for (int i = 1; i <= n; i++) {
if (find(i) != find(tt)) {
ax[find(i)] = tt;
unio(find(i), tt);
sum++;
}
}
printf("%d\n", sum);
for (int i = 1; i <= n; i++) {
printf("%d ", ax[i]);
}
printf("\n");
return 0;
}