题意:给定长度为n的排列,一次操作你可以选择任意个下标数对:(x1,y1),(x2,y2),…(xn,yn)。
要求每个下标最多只出现一次。问最少需要几次操作能够将排列恢复为原排列,同时输出每次操作选择的下标。
思路:
之间连一条边,如果不形成环,那么一次操作就行,如果环上只有两个点,那么也只需要一次。
如果环中有个点
,那么两次交换即可,只需要维护两个游标从首尾开始交换,把大环拆成若干个只有两个点的小环和若个排好序的点,然后再操作一次拆掉2个点的小环就行了。
MyCode:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7,maxm=1e6+7,mod=1e9+7;
typedef long long int ll;
typedef unsigned long long ull;
int a[maxn];
bool vis[maxn];
vector<int>ans[2],tmp;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=1; i<=n; ++i)
cin>>a[i];
for(int i=1,now; i<=n; ++i) {
if(vis[i]) continue;
now=i;
tmp.clear();
tmp.emplace_back(now);
vis[now]=1;
while(!vis[a[now]]) {
now=a[now];
vis[now]=true;
tmp.emplace_back(now);
}
for(int j=0,k=tmp.size()-1; j<k; ++j,--k) {
ans[0].emplace_back(tmp[j]);
ans[0].emplace_back(tmp[k]);
}
for(int j=1,k=tmp.size()-1; j<k; ++j,--k) {
ans[1].emplace_back(tmp[j]);
ans[1].emplace_back(tmp[k]);
}
}
int cnt(0);
for(int i=0; i<2; ++i)
if(ans[i].size()) cnt=i+1;
else break;
cout<<cnt<<'\n';
for(int i=0; i<cnt; ++i) {
cout<<ans[i].size()/2<<' ';
for(int k:ans[i]) cout<<k<<' ';
cout<<'\n';
}
return 0;;
} 
京公网安备 11010502036488号