題目:
給定過一個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;
}