1、逆序对的定义
什么是逆序对呢?逆序对指的是在一个序列中,有x、y,满足x<y且a[x]>a[y],那么a[x]、a[y]就是一个逆序对。
我们关于逆序对主要是求一个序列中逆序对的个数。
2、如何求逆序对
考虑朴素算法,对于每个x,y考虑一遍就好了,代码如下(其实我不太想打):
#include<bits/stdc++.h> using namespace std; int n,a[1000001],ans,i,j; int main() { scanf("%d",&n); for (i=1;i<=n;i++) scanf("%d",&a[i]); ans=0; for (i=1;i<=n;i++) for (j=i;j<=n;j++) if (a[i]>a[j]) ans++;//判断大小关系 pribntf("%d",ans); return 0; }
明显的,这是一个O(n2)的算法,在一些n较大的题目中并不可用。
有什么优化方法吗?
归并排序(然而博主并没有学这玩意儿)
树状数组(然而博主看不懂这玩意儿)
主席树(然而博主并不认为你想用这玩意儿求逆序对)
那就是线段树
线段树怎么求逆序对呢?
普通的线段树求的是[l,r]之间的元素之和,而我们这次是可以记录数值在[l,r]之间的数的个数,从后往前扫,边扫边加,这样就可以求出逆序对了(而且还可以求出每个点的逆序对个数等等)。
注意要离散化。
以下是代码:
#include<bits/stdc++.h> using namespace std; long long z[2000001],l[2000001],r[2000001],n,d[500001],ans,i,cnt; struct cjy{ long long z,q; }c[500001]; bool cjj(cjy a,cjy b)//我不会告诉你为什么结构体类型名和判断函数名叫这个 { return a.z<b.z; } inline void build(long long u,long long l1,long long r1)//线段树 { l[u]=l1; r[u]=r1; if (l1==r1) return; build(u*2,l1,(l1+r1)/2); build(u*2+1,(l1+r1)/2+1,r1); } inline void jia(long long u,long long l1,long long r1,long long k) { if ((l[u]>r1)||(r[u]<l1)) return; if ((l[u]>=l1)&&(r[u]<=r1)) { z[u]+=k*(r[u]-l[u]+1); return; } jia(u*2,l1,r1,k); jia(u*2+1,l1,r1,k); z[u]=z[u*2]+z[u*2+1]; } inline long long qui(long long u,long long l1,long long r1) { if ((l1>r[u])||(r1<l[u])) return 0; if ((l1<=l[u])&&(r1>=r[u])) return z[u]; return (qui(u*2,l1,r1)+qui(u*2+1,l1,r1)); } int main() { scanf("%lld",&n); for (i=1;i<=n;i++) { scanf("%lld",&c[i].z); c[i].q=i; } sort(c+1,c+n+1,cjj); build(1,1,n); cnt=0; for (i=1;i<=n;i++) { if (c[i].z>c[i-1].z) cnt++;//离散化 d[c[i].q]=cnt; } for (i=n;i>=1;i--) { ans+=qui(1,1,d[i]-1); jia(1,d[i],d[i],1); } printf("%lld",ans); return 0; }
3、求逆序对的应用
逆序对的应用还是比较多的,比如康托展开等。