题意:给出一个长度为n的序列p,若p[u]跟p[v]连边,那么u跟v就要连边,问是否可以建造一颗结点数为n的树。
做法:考虑下置换群,不懂的话没关系,拿第一个样例来说
4 3 2 1
可以拆成(4,1)跟(3,2),为啥这么拆?
因为4转化到1,1转化到4,所以丢到一起,大概称之为一个群?算了就叫做集合吧。
3跟2同理。
总之就是,一个集合里的数跟它能够转化得到的数都在同一个集合里。
对于一个序列,我们把它拆成很多没有交集的集合。
下面来建树,步骤是这样的,
4跟3连边
4转化到1,3转化到2
1跟2连边,
1转化到4,2转化到3,
4跟3连边,已经连过了,ok,结束。此时,只要将4跟1连边一棵树就搞定了,推广一下可以发现若第一个集合的容量大于2必然无解,因为第一个集合中的点若相互连边,无论怎样都将成环。
可以看到,若继续下去,也就是(3,2)变成(3,2,x),x是未知数,
那么就变成,
1转化到4,2转化到x,
4跟x连边,
4转化到1,x转化到3,
1跟3连边,
1转化到4,3转化到2,
2跟4连边,成环,无解,推广一下发现若第二个集合容量为奇数则必成环。
这里要提出一个概念,好比这个样例,1能转化到4,4能转化到1,我们称1跟4成环,同理,(3,2,x)也成环,然而不是每一个群都刚好是个环,它可以是个“6”的形状,也就是带尾巴的环,例如(3,2,x)的时候,x转化到2而不是3。所以只能说群中必然恰好有一个环,(1)是自环,环长为1,(1,4)的环长是2,变化前的(3,2,x)环长是3,变化后的环长是2,以此类推。
用这个方法去进行思考跟构造,最终的结论就是若将这个序列拆成若干个群后,当有一个群的容量小于3,且其它容量大于1的群中环长都是偶数时,才有解。
做法:考虑下置换群,不懂的话没关系,拿第一个样例来说
4 3 2 1
可以拆成(4,1)跟(3,2),为啥这么拆?
因为4转化到1,1转化到4,所以丢到一起,大概称之为一个群?算了就叫做集合吧。
3跟2同理。
总之就是,一个集合里的数跟它能够转化得到的数都在同一个集合里。
对于一个序列,我们把它拆成很多没有交集的集合。
下面来建树,步骤是这样的,
4跟3连边
4转化到1,3转化到2
1跟2连边,
1转化到4,2转化到3,
4跟3连边,已经连过了,ok,结束。此时,只要将4跟1连边一棵树就搞定了,推广一下可以发现若第一个集合的容量大于2必然无解,因为第一个集合中的点若相互连边,无论怎样都将成环。
可以看到,若继续下去,也就是(3,2)变成(3,2,x),x是未知数,
那么就变成,
1转化到4,2转化到x,
4跟x连边,
4转化到1,x转化到3,
1跟3连边,
1转化到4,3转化到2,
2跟4连边,成环,无解,推广一下发现若第二个集合容量为奇数则必成环。
这里要提出一个概念,好比这个样例,1能转化到4,4能转化到1,我们称1跟4成环,同理,(3,2,x)也成环,然而不是每一个群都刚好是个环,它可以是个“6”的形状,也就是带尾巴的环,例如(3,2,x)的时候,x转化到2而不是3。所以只能说群中必然恰好有一个环,(1)是自环,环长为1,(1,4)的环长是2,变化前的(3,2,x)环长是3,变化后的环长是2,以此类推。
用这个方法去进行思考跟构造,最终的结论就是若将这个序列拆成若干个群后,当有一个群的容量小于3,且其它容量大于1的群中环长都是偶数时,才有解。
建造树就可以挑一个容量小于3且容量最小的群为父亲不断跟其它群连边,最后将这个父亲群内部连边即可。
#include<map>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<climits>
#include<list>
#include<iomanip>
#include<stack>
#include<set>
using namespace std;
int bx[2],p[100010],vis[100010];
bool in[100010];
vector<int>ans;
bool dfs(int pos,int step)
{
vis[pos]=step;
ans.push_back(pos);
if(!vis[p[pos]])
return dfs(p[pos],step+1);
int t=step+1-vis[p[pos]];
return bx[0]==bx[1]||t==1||t%2==0;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",p+i);
in[p[i]]=1;
if(i==p[i])
bx[0]=bx[1]=i;
}
if(bx[0]==0)
for(int i=1;i<=n;i++)
if(i==p[p[i]])
{
bx[0]=i;
bx[1]=p[i];
break;
}
if(bx[0]==0)
puts("NO");
else
{
vis[bx[0]]=vis[bx[1]]=1;
bool flag=1;
for(int i=1;flag&&i<=n;i++)
if(!in[i])
flag=dfs(i,1);
if(!flag)
{
puts("NO");
return 0;
}
for(int i=1;flag&&i<=n;i++)
if(!vis[i])
flag=dfs(i,1);
if(!flag)
{
puts("NO");
return 0;
}
puts("YES");
if(bx[0]!=bx[1])
printf("%d %d\n",bx[0],bx[1]);
int len=ans.size();
for(int i=0;i<len;i++)
printf("%d %d\n",bx[i&1],ans[i]);
}
}