题解 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;
}