题目:
给定过一个1到N的排列P1到PN ,请判断是否存在一个由N个点,N-1条边组成的无向连通图,满足对于任意两个整数i和j(i不等于j),若第i个点和第j个点之间有边相连,则第Pi个点和第Pj个点之间同样有边相连。
输入:
第一行输入一个整数T表示T组数据,
每组数据格式为:
第一行包括一个整数N,
第二行包含N个整数Pi到PN
输出:
每组输出一行,如果存在满足条件的图则输出Yes,否则输出No
样例输入:
2
4
4 3 2 1
3
3 1 2
样例输出:
Yes
No
解析:
将排列分解成多个环,那就存在以下几种情况:
当最小环长度是1时,即至少有一个元素是在自己的位置,此时将这个点与其他n-1个点相连,就可以满足要求;
当最小环长度是2时,只有在剩余所有环的长度都是偶数时,才能满足要求,这时对应的连接方式是最小环的两个点之间有连线,其他环中,一半点和最小环中的第一个点相连,另一半点和最小环中另一个点相连;
当最小环的长度大于2时,这时无法满足要求。
代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1e5+5;
int p[N];
bool vis[N];
vector<int> v;
int get_cycle_len(int cur, int cnt, int st){
if(cur == st){
return cnt + 1;
}else{
return get_cycle_len(p[cur], cnt+1, st);
}
}
int main(){
int t, n;
cin>>t;
while(t--){
cin>>n;
for(int i = 1; i <= n; i++)
cin>>p[i];
memset(vis, 0, sizeof(vis));
v.clear();
for(int i = 1; i <= n; i++){
if(!vis[i]){
v.push_back(get_cycle_len(p[i], 0, i));
}
}
sort(v.begin(), v.end());
bool ans;
if(v[0] == 1){
ans = true;
} else if(v[0] == 2){
ans = true;
for(int i = 1; i < v.size(); i++){
if(v[i] % 2){
ans = false;
break;
}
}
}else{
ans = false;
}
if(ans) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}