题解 CF1375E Inversion SwapSort(构造)

题目链接

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

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

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

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

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

我们怎么做呢?可以依次操作:(( ext{pos}[a[n]+1],n)), (( ext{pos}[a[n]+2],n)), (( ext{pos}[a[n]+3],n)) ,......, (( ext{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;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/13246526.html