ARC 124 D Yet Another Sorting Problem 题解

这篇博客探讨了一种排列问题,其中涉及到在给定的排列中,通过选择前N个数中的一个和后M个数中的一个进行交换,来达到目标排列1,2,3,...,N+M。作者首先介绍了没有交换限制时的解决方案,然后讨论了当限制交换必须在排列的左右两侧之间进行时的情况。通过对图论建模和分析连通块,得出了解决问题的算法,时间复杂度为O(n)。最后,提供了C++代码实现来解决这个问题。

Description

给定一个长度为 N+MN+MN+M 的排列。

你每次可以选择前 NNN 个数中的一个以及后 MMM 个数中的一个并将它们交换。

你需要求出,至少需要多少次交换操作才能使得排列变为 1,2,3,⋯ ,N+M1,2,3,\cdots,N+M1,2,3,,N+M

Solution

下面,为方便叙述,令位置不超过 NNN左边,否则为右边

假设我们可以交换同属一边的数,那么这就成为了一道裸题: 从 iiipip_ipi 连边,得到了一张每个点的入度以及出度均为 111 的图。考虑其每一个连通块(显然是个环),并交换环上相邻的位置即可。总操作次数为 N+M−KN+M-KN+MK,其中 KKK 表示连通块的个数。

加入只能交换左右边的限制怎么办呢?我们依然按照 i→pii \to p_iipi 的方式建图。考虑每一个连通块,若它跨越了左边和右边,那么它必然可以S−1S-1S1 次交换中被还原,其中 SSS 表示连通块的大小。特别的,若它只属于左边或只属于右边,那么上述结论不成立。

令只属于左边的连通块有 XXX 个,只属于右边的连通块有 YYY 个。不妨设 X<YX<YX<Y。我们可以将左边的前 YYY 个与右边的两两配对并进行操作,且每次操作次数均为 SSS,其中 SSS 表示混合连通块的大小(先随意交换一个位置,然后接下来每次都可以恰好复原一个位置,最后一次可以复原两个位置,1+(S−1)=S1+(S-1)=S1+(S1)=S)。对于那 Y−XY-XYX 个失配的,我们从异侧硬搞过来一个位置与它配对并进行操作。分析一下这两种的优劣——对于混合连通块而言,若 S=x+yS=x+yS=x+y,我们默认操作次数是 x−1+y−1x-1+y-1x1+y1 然而这里是 x+yx+yx+y,所以总数要加上 222。对于单个连通块而言,令其大小为 xxx,我们找过来了一个使得 S=x+1S=x+1S=x+1,然而我们默认是 x−1x-1x1,所以总数也要加上 222

综上所述,答案为 N+M−K+2max⁡(X,Y)N+M-K+2\max(X,Y)N+MK+2max(X,Y)

时间复杂度 O(n)O(n)O(n),本题被解决。

Code

#include <bits/stdc++.h>
using namespace std;
const int maxl=200005;

int read(){
	int s=0,w=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-')  w=-w;ch=getchar();}
	while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
	return s*w;
}
int n,m,len,k,x,y;
int a[maxl],fa[maxl],cntl[maxl],cntr[maxl];

int Find(int x){
	if (x==fa[x])  return x;
	else return fa[x]=Find(fa[x]);
}

signed main(){
	n=read(),m=read();len=n+m;
	for (int i=1;i<=len;i++){
		a[i]=read(),fa[i]=i;
		if (i<=n)  cntl[i]=1;
		else cntr[i]=1;
	}
	for (int i=1;i<=len;i++){
		int fu=Find(i),fv=Find(a[i]);
		if (fu!=fv){
			fa[fu]=fv;
			cntl[fv]+=cntl[fu];
			cntr[fv]+=cntr[fu];
		}
	}
	for (int i=1;i<=len;i++){
		if (i==Find(i)){
			k++;
			if (cntl[i]+cntr[i]==1)  continue;
			if (cntr[i]==0)  x++;
			if (cntl[i]==0)  y++;
		}
	}
	cout<<len-k+2*max(x,y)<<endl;
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值