题解 CF1375E Inversion SwapSort(构造)

题目链接

考虑\(a\)是一个排列时怎么做。

我们设\(\text{pos}[v]\)表示\(v\)这个数在\(a\)里出现的位置。也就是\(\text{pos}[a[i]]=i\)

从边界入手,我们先尝试把\(n\)放到排列的最后一个位置,然后转化为规模减\(1\)的子问题。具体来说,假设一波操作后,我们得到的排列为\(b\),则\(b\)需要满足如下条件:

  • \(b[n]=n\)
  • \(\forall 1\leq i,j<n\),如果\(a[i]<a[j]\),则\(b[i]<b[j]\)
  • \(\forall1\leq i,j<n\),如果\(a[i]>a[j]\),则\(b[i]>b[j]\)

也就是说,要保证前面的数的相对大小关系不变,这样才能转化为一个等价的子问题。

我们怎么做呢?可以依次操作:\((\text{pos}[a[n]+1],n)\), \((\text{pos}[a[n]+2],n)\), \((\text{pos}[a[n]+3],n)\) ,......, \((\text{pos}[n],n)\)。容易发现,这样一轮操作完成后,首先,\(n\)被放到了最后。同时,前面所有大于\(a[n]\)的数,相当于集体减\(1\),显然前面所有数的相对大小关系不变。并且,我们恰好用掉了所有包含(位置)\(n\)的逆序对。所以剩下的是一个规模减\(1\)的子问题,继续做,直到\(n=1\)即可。

于是我们就解决了排列的情况。相当于我们用构造的方法证明了,\(a\)是一个排列时,一定有解。

再考虑不是排列的时候。对于两个相等的数\(a[i]=a[j]\) (\(i<j\)),我们强行令\(a[i]<a[j]\),也就是以数值为第一关键字,位置为第二关键字,强行转成一个排列。发现转成排列后,序列里的逆序对和原来是一样的,所以直接按排列求解即可。

时间复杂度\(O(n^2)\)

参考代码:

//problem:CF1375E
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int MAXN=1000;
int n,a[MAXN+5],vals[MAXN+5],cnt_val,p[MAXN+5],pos[MAXN+5];
vector<pii>ans;
void work(int p1,int p2){
	assert(pos[a[p1]]==p1);assert(pos[a[p2]]==p2);
	pos[a[p1]]=p2;
	pos[a[p2]]=p1;
	swap(a[p1],a[p2]);
	ans.pb(mk(p1,p2));
}
int main() {
	cin>>n;
	for(int i=1;i<=n;++i)cin>>a[i],vals[++cnt_val]=a[i];
	sort(vals+1,vals+cnt_val+1);
	cnt_val=unique(vals+1,vals+cnt_val+1)-(vals+1);
	int x=0;
	for(int v=1;v<=cnt_val;++v){
		for(int i=1;i<=n;++i)
			if(a[i]==vals[v])
				p[i]=++x;
	}
	for(int i=1;i<=n;++i)a[i]=p[i];
	assert(x==n);
	//于是a变成了一个排列
	for(int i=1;i<=n;++i)pos[a[i]]=i;
	for(int i=n;i>=1;--i){
		for(int j=a[i]+1;j<=i;++j){
			work(pos[j],i);
		}
	}
	cout<<SZ(ans)<<endl;
	for(int i=0;i<SZ(ans);++i)cout<<ans[i].fi<<" "<<ans[i].se<<endl;
	return 0;
}
posted @ 2020-07-05 16:22  duyiblue  阅读(703)  评论(3编辑  收藏  举报