CodeForces 698B Fix a Tree
并查集,构造。
先看一下图的特殊性,按照这种输入方式,一个点的入度最多只有$1$,因此,问题不会特别复杂,画画图就能知道了。
如果给出的序列中已经存在$a[i]=i$,那么随便取一个$a[i]=i$的$i$作为$root$,剩下的每一条边$a[i] \to i$,可以用并查集来处理,如果发现某条边$a[i] \to i$加入前$a[i]$与$i$已经在同一集合中,说明再加$a[i] \to i$会导致成环,因此将$i$的$father$改成$root$即可,并将$i$与$root$合并。
如果给出的序列中不存在$a[i]=i$,和上面的处理方法类似,唯一不同的是:找到第一条不能加入的边$a[i] \to i$,将$i$的$father$改为$i$,并且$root$设置为$i$,之后出现不能加入的边和上面处理方法一样。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<iostream> using namespace std; typedef long long LL; const double pi=acos(-1.0),eps=1e-8; void File() { freopen("D:\\in.txt","r",stdin); freopen("D:\\out.txt","w",stdout); } template <class T> inline void read(T &x) { char c = getchar(); x = 0;while(!isdigit(c)) c = getchar(); while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); } } const int maxn=200010; int f[maxn],a[maxn],n; int Find(int x) { if(x!=f[x]) f[x]=Find(f[x]); return f[x]; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int root=-1; for(int i=1;i<=n;i++) if(a[i]==i) root=i; int num=0; for(int i=1;i<=n;i++) f[i]=i; for(int i=1; i<=n; i++) { if(i==root) continue; int fx=Find(i),fy=Find(a[i]); if(fx!=fy) f[fx]=fy; else { if(root==-1) a[i]=i, root=i, num++; else { a[i]=root, num++; fx=Find(root); fy=Find(i); f[fx]=fy; } } } printf("%d\n",num); for(int i=1; i<=n; i++) printf("%d ",a[i]); printf("\n"); return 0; }