codeforces 576B Invariance of Tree

本文探讨了如何根据一个整数序列p判断能否构建一颗特定的树,通过将序列拆分成若干个没有交集的集合,并利用这些集合的特性来判断解的存在性,最后给出了具体的构建方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给出一个长度为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的群中环长都是偶数时,才有解。


建造树就可以挑一个容量小于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]);
	}
}

time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

A tree of size n is an undirected connected graph consisting of n vertices without cycles.

Consider some tree with n vertices. We call a tree invariant relative to permutation p = p1p2... pn, if for any two vertices of the tree u and vthe condition holds: "vertices u and v are connected by an edge if and only if vertices pu and pv are connected by an edge".

You are given permutation p of size n. Find some tree size n, invariant relative to the given permutation.

Input

The first line contains number n (1 ≤ n ≤ 105) — the size of the permutation (also equal to the size of the sought tree).

The second line contains permutation pi (1 ≤ pi ≤ n).

Output

If the sought tree does not exist, print "NO" (without the quotes).

Otherwise, print "YES", and then print n - 1 lines, each of which contains two integers — the numbers of vertices connected by an edge of the tree you found. The vertices are numbered from 1, the order of the edges and the order of the vertices within the edges does not matter.

If there are multiple solutions, output any of them.

Sample test(s)
input
4
4 3 2 1
output
YES
4 1
4 2
1 3
input
3
3 1 2
output
NO
Note

In the first sample test a permutation transforms edge (4, 1) into edge (1, 4), edge (4, 2) into edge (1, 3) and edge (1, 3) into edge (4, 2). These edges all appear in the resulting tree.

It can be shown that in the second sample test no tree satisfies the given condition.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值