修树
题意:
- 给你一颗树的 边 关系( i 点 与 P i P_i Pi 之间有一条边 ),有可能 边 关系有错,问你用最少的更改次数让这颗树合法,输出更改后的 关系。
- 如果 i = = P i i == P_i i==Pi ,表示这个点是根节点。
思路:
- 先贪心,本来已经合法的关系我们显然不需要变动,先连结。标记所有合并过程中不合法的关系。
- 之后对于不合法的关系,我们找到一个合法的(也就是和它不在同一个连通块中,因为在同一个块中再加一条边就存在环了,不合法)其他块与它连结。
- 如果暴力找的话会 n 2 n^2 n2 超时,可以用双指针优化这过程。
C o d e : Code: Code:
#include<bits/stdc++.h>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
#define all(a) a.begin(),a.end()
#define oper(a) (operator<(const a& ee)const)
#define endl "\n"
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
double DNF = 1e17;
const int N = 200010, M = 200010, MM = 110;
int INF = 0x3f3f3f3f, mod = 1e9 + 7;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D, K;
int z[N], p[N];
bool st[N];
int find(int x) {
return p[x] == x ? p[x] : p[x] = find(p[x]);
}
void solve() {
cin >> n;
forr(i, 1, n)cin >> z[i], p[i] = i;
int id = -1, cnt = 1, res = 0;
forr(i, 1, n) { //合并所有合法边
int fa = find(i), fb = find(z[i]);
if (i == z[i]) {
if (id == -1)id = i;//根节点只能有一个
else st[i] = true;
}
else if (fa ^ fb) {
p[fa] = fb;
}
else st[i] = true;
}
forr(i, 1, n) { //再处理不合法
if (st[i]) {
while (find(cnt) == i)cnt++;//找到不在同一块的点
if (cnt > n)cnt = i;
//这里需要特判下根节点,如果怎么都找不到证明这个点是根节点
z[i] = cnt;
p[find(i)] = find(cnt);
res++;
}
}
cout << res << endl;
forr(i, 1, n)cout << z[i] << ' ';
}
int main() {
cinios;
T = 1;
while (T--)solve();
return 0;
}
/*
7
4 3 2 6 3 5 2
*/