题意:
给定你一个长度为
n
n
n的排列。
你一共可以进行若干轮的操作,每一轮的操作是,你在这一轮中可以选择若干对的下标位置
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
.
.
.
,
(
x
k
,
y
k
)
(x_1,y_1),(x_2,y_2),...,(x_k,y_k)
(x1,y1),(x2,y2),...,(xk,yk)然后这
2
∗
k
2 * k
2∗k个数必须要两两不相同,即一个数最多只能出现一次,然后问你最少能经过多少轮操作,使得这个初始的排列变得有序起来。
思路:
当时比赛的时候,不知道
p
a
i
r
w
i
s
e
d
i
s
t
i
n
c
t
pairwise \ distinct
pairwise distinct具体的意思,以为是只要不是两两对应相等就行,不过为什么高阶牛津都查不到啊,人麻了…,本来做到这就不到一个小时了,还读错题了…,人没了。
多手写几组数据后,会发现,给定的一组排列中,会有若干个数之间形成一个环,例如 51234 5 1 2 3 4 51234, 5 − 4 − 3 − 2 − 1 − 5 5-4-3-2-1-5 5−4−3−2−1−5,就是顺着当前这个数去找它对应的位置看那个数是几,知道形成回路,这样 n n n个数,最终就会形成若干个长度 ≥ 2 \geq2 ≥2的环。
1.
1.
1.若环的长度
=
2
=2
=2,直接交换一次,他们二者就会回到对应位置上。
2.
2.
2.若环的长度大于
>
2
>2
>2,例如
5
−
4
−
3
−
2
−
1
5 -4-3-2-1
5−4−3−2−1,此时我们如果以3为中心,交换首尾两个位置,这样我们能保证,首位置一定是被放好的了,然后再以中心为对称继续交换,我们就可以发现,对于本来
1
−
5
,
5
−
4
,
2
−
1
1-5,5-4,2-1
1−5,5−4,2−1这一组,
1
,
5
1,5
1,5交换完以后,
5
,
2
5,2
5,2就变成了我们情况
1.
1.
1.的长度=2的环,而剩下的每组其实都会这样,所以我们再先这样对称的换一次,然后在经过最多一轮变化,就可以都变成有序的了。
至此我们最多需要两轮即可完成游戏,分类讨论一下即可。
如果不是很明白,建议自己手模几个实例,看一看
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
int p[N],pos[N],cnt;//初始排列数组 和 位置数组
std::vector<int>circle[N];//存放每个环内的数是多少
std::vector<int>ans[2];
bool vis[N];
void work(int x) {//找环
int cur = x;
while(!vis[cur]) {
circle[cnt].eb(cur);
vis[cur] = true;
cur = pos[cur];
}
}
int main() {
int n;scanf("%d",&n);
int fg = 1;
for(int i = 1;i <= n;i ++) {
scanf("%d",&p[i]);
pos[i] = p[i];
if(p[i] != i) fg = 0;
}
if(fg) return printf("0\n"),0;
fg = 0;
for(int i = 1;i <= n;i ++) {
if(p[i] != i && !vis[i]) {
++cnt;
work(i);
// for(int j = 0;j < sz(circle[cnt]);j ++) cout << circle[cnt][j] << ' '; cout << '\n';
if(sz(circle[cnt]) > 2) fg = 1;
}
}
if(!fg) {//所有的环没有大于2的即可一次完成
printf("1\n");
printf("%d",cnt);
for(int i = 1;i <= cnt;i ++) {
for(int j = 0;j < sz(circle[i]);j ++) {
printf(" %d",circle[i][j]);
}
}
printf("\n");
}
else {
printf("2\n");
for(int i = 1;i <= cnt;i ++) {
if(sz(circle[i]) <= 2) {//先把一次能换完的处理一下
for(int j = 0;j + 1 < sz(circle[i]);j += 2) {
ans[0].eb(circle[i][j]),ans[0].eb(circle[i][j + 1]);
swap(p[circle[i][j]],p[circle[i][j+1]]);
}
}
}
for(int i = 1;i <= cnt;i ++) {
if(sz(circle[i]) > 2) {
for(int l = 0,r = sz(circle[i]) - 1;l < r && l < sz(circle[i]);l ++,r--) {
int p1 = circle[i][l],p2 = circle[i][r];
ans[0].eb(p1),ans[0].eb(p2);
swap(p[p1],p[p2]);
pos[p1] = p[p1];
pos[p2] = p[p2];
}
}
}
for(int i = 1;i <= n;i ++) {
if(p[i] != i) {
ans[1].eb(i),ans[1].eb(p[i]);
swap(p[i],p[p[i]]);
}
}
printf("%d",sz(ans[0]) / 2); for(int i = 0;i < sz(ans[0]);i ++) printf(" %d",ans[0][i]); printf("\n");
printf("%d",sz(ans[1]) / 2); for(int i = 0;i < sz(ans[1]);i ++) printf(" %d",ans[1][i]); printf("\n");
}
return 0;
}