逆序对相关的题目:
题目1: 逆序对模板题: https://www.acwing.com/problem/content/description/790/
题目2: 百练平台的重要逆序对:http://bailian.openjudge.cn/xly2018/E/
逆序对问题的处理方法:
1.(推荐): 分治法,归并排序的处理思路
2. 哈希+树状数组
注意: 个人比较推荐第一种逆序对的做法,比较具有普适性. 哈希+树状数组的做法在遇到一些特定的题目(e.g. 题目2)的时候会相对比较难以处理.
主要来讲一讲"分治法"
分治法的主要思路就是讲规模为n的问题拆分为左半部分和右半部分, 递归处理这两部分. 然后对这两部分进行合并(归并). 应用到逆序对中, 我们可以将整个序列的逆序对划分为以下3类:
1. 左半部分的逆序对
2. 右半部分的逆序对
3. 横跨两边的逆序对
其中1和2可以由递归函数处理得到,只需要考虑3如何统计.
常规的暴力统计的方法去统计3,复杂度是O(n^2). 复杂度高的原因: 没有充分利用序列信息.
注意到: 3是统计横跨两边的逆序对,与左半部分序列的内部顺序无关,右半部分同理.
事实上,我们可以规定我们的递归函数的副作用为: 对于一个序列,先做逆序对的统计,然后再将这个序列排序.
那么在递归处理左半部分和右半部分之后,我们得到的左半部分和右半部分的序列都是有序的.
然后我们用双指针统计横跨两边的逆序对即可. 这样的时间复杂度是O(n)的.
因此,总的时间复杂度就是O(nlogn).
这种逆序对的处理方法,是具有一定的普适性的,对于题目2,只需要将双指针的移动条件进行相应的修改即可.
题目1的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
int a[N];
int b[N],c[N];
int n;
ll merge(int l,int r){
// the merge opts to 1. calculate the number of inverse pair
// 2. to sort the [l, r] of the array.
if(l>r||l==r) return 0;
int mid=l+r>>1;
ll l_inverse=merge(l,mid);
ll r_inverse=merge(mid+1,r);
int lp=l,rp=mid+1;
ll res=0;
res=l_inverse+r_inverse;
while(lp<=mid){
while(rp<=r&&a[rp]<a[lp]) rp++;
res+=(rp-1)-(mid+1)+1;
lp++;
}
lp=l,rp=mid+1;
int cnt=l;
while(lp<=mid&&rp<=r){
if(a[lp]<a[rp]) b[cnt++]=a[lp++];
else b[cnt++]=a[rp++];
}
while(lp<=mid) b[cnt++]=a[lp++];
while(rp<=r) b[cnt++]=a[rp++];
for(int i=l;i<=r;i++) a[i]=b[i];
return res;
}
int main(void){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ll res=merge(1,n);
printf("%lld",res);
return 0;
}
题目2的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
int a[N];
int b[N],c[N];
int n;
ll merge(int l,int r){
// the merge opts to 1. calculate the number of inverse pair
// 2. to sort the [l, r] of the array.
if(l>r||l==r) return 0;
int mid=l+r>>1;
ll l_inverse=merge(l,mid);
ll r_inverse=merge(mid+1,r);
int lp=l,rp=mid+1;
ll res=0;
res=l_inverse+r_inverse;
while(lp<=mid){
while(rp<=r&&2*a[rp]<a[lp]) rp++;
res+=(rp-1)-(mid+1)+1;
lp++;
}
lp=l,rp=mid+1;
int cnt=l;
while(lp<=mid&&rp<=r){
if(a[lp]<a[rp]) b[cnt++]=a[lp++];
else b[cnt++]=a[rp++];
}
while(lp<=mid) b[cnt++]=a[lp++];
while(rp<=r) b[cnt++]=a[rp++];
for(int i=l;i<=r;i++) a[i]=b[i];
return res;
}
int main(void){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ll res=merge(1,n);
printf("%lld",res);
return 0;
}