求逆序对的两种常用方法
首先我们得理解一下什么是逆序对。在一个数列$a$中,满足$a[i]>a[j]$并且$i<j$的数对就叫做逆序对。
一般求逆序对常用的有两种方法,归并排序和树状数组。(我个人比较喜欢归排)这两样不会的请出门左转百度。
下面就讲讲这两种方法:
首先讲讲归排求逆序对。归并排序用到了二分的思想,在排序过程中如果$a[i]<=a[j]$就不会产生逆序对,如果$a[i]>a[j]$就会产生$mid-i+1$个逆序对,因为做归排的时候$l~mid$和$mid+1~r$都是已经排好序的所以如果$a[i]>a[j]$那么$a[i+1]~a[mid]$也就都大于$a[j]$,这个非常容易懂,下面是代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int N=40005; int n,a[N],c[N],ans; void ready() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; } inline void msort(int l,int r) { if(l==r)return; int mid=(l+r)>>1; msort(l,mid);msort(mid+1,r); int i=l,j=mid+1,k=l; while(i<=mid&&j<=r){ if(a[i]<=a[j]) c[k++]=a[i++]; else{ c[k++]=a[j++]; ans+=(mid-i+1); } } while(i<=mid)c[k++]=a[i++]; while(j<=r)c[k++]=a[j++]; for(i=l;i<=r;i++) a[i]=c[i]; } void work() { msort(1,n); cout<<ans<<endl; } int main() { std::ios::sync_with_stdio(false); ready();work();return 0; }
再来讲讲树状数组,这个可能比归排的要不那么容易理解。使用树状数组求逆序对还需要进行离散化操作(因为数据过大的话无法直接开数组),对于离散后的序列进行一次遍历,遍历过程中就向树状数组C进行插入操作(每次插入的值为1),这里树状数组表示的是在该元素前面但是比该元素大的元素个数,进行插入操作以后就查询。。。我觉得看代码貌似会容易理解一点。。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<iostream> using namespace std; const int N=40005; int n,b[N],c[N],ans; struct node{int v,id;}a[N]; inline int lowbit(int x) {return x&(-x);} inline void insert(int i,int x) { while(i<=n){ c[i]+=x;i+=lowbit(i); } } inline int getnum(int x) { int ret=0,i=x; while(i>0){ ret+=c[i];i-=lowbit(i); } return ret; } inline bool cmp(node x,node y) {return x.v<y.v;} void work() { cin>>n; for(int i=1;i<=n;i++){ cin>>a[i].v; a[i].id=i; } sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++)b[a[i].id]=i; for(int i=1;i<=n;i++){ insert(b[i],1); ans+=i-getnum(b[i]); } cout<<ans<<endl; } int main() { std::ios::sync_with_stdio(false); work();return 0; }
蒟蒻写博客不易,如果有误还请大佬们提出
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com