2020ICPC昆明 J - Parallel Sort 思维 + 构造

题意:
给定你一个长度为 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 2k个数必须要两两不相同,即一个数最多只能出现一次,然后问你最少能经过多少轮操作,使得这个初始的排列变得有序起来。

思路:
当时比赛的时候,不知道 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 543215,就是顺着当前这个数去找它对应的位置看那个数是几,知道形成回路,这样 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 54321,此时我们如果以3为中心,交换首尾两个位置,这样我们能保证,首位置一定是被放好的了,然后再以中心为对称继续交换,我们就可以发现,对于本来 1 − 5 , 5 − 4 , 2 − 1 1-5,5-4,2-1 15,54,21这一组, 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值