逆序数是这样定义的:有一个序列n1,n2,n2....,对于序列中每一个元素i来说,排在其前面的数中(即1到i-1中的数),比ni大的元素的个数的总和,叫做这个序列的逆序数。
通俗点说就是,找出序列中每一个满足的这样一种关系的i和j的对数,就是对于i>j&&a(i)<a(j)。比如1,3,2,5,4这个序列,其逆序数为2,即3和2,5和4。由定义我们知道,对于一个递增序列,其逆序数显然为0。
逆序数有一个非常常用的性质:最少需经过m次交换,可以使一个序列变成升序。其中m为该序列的逆序数。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2689
分析:我们用线段树来纪录每一个区间内的数字的个数,比如[3,8]这个区间,纪录的是3,4,5,6,7,8中已经出现的数的个数,这样我们在每一次更新之后就进行一次查询,找出当前元素后面有多少个元素已经插入到线段树中了,这些元素的个数就是该数的逆序数,对于每一个元素,我们累加起来即可。
线段树实现代码如下:
#include <iostream> #include <cstdio> using namespace std; const int maxn=5005; struct segment { int l,r; int num; }tree[maxn<<2]; void build(int root,int l,int r) { tree[root].l=l; tree[root].r=r; tree[root].num=0; if(l==r) return ; int mid=(l+r)>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); } void update(int root,int v) { if(tree[root].l==v&&tree[root].r==v) { tree[root].num=1; return ; } int mid=(tree[root].l+tree[root].r)>>1; if(v<=mid) update(root<<1,v); else update(root<<1|1,v); tree[root].num=tree[root<<1].num+tree[root<<1|1].num; } int query(int root,int l,int r) { if(l<=tree[root].l&&r>=tree[root].r) return tree[root].num; int mid=(tree[root].l+tree[root].r)>>1; int sum1=0,sum2=0; if(l<=mid) sum1=query(root<<1,l,r); if(r>mid) sum2=query(root<<1|1,l,r); return sum1+sum2; } int main() { int n,a[maxn]; while(scanf("%d",&n)!=-1) { build(1,1,n); int ans=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); ans+=query(1,a[i]+1,n); update(1,a[i]); } printf("%d\n",ans); } return 0; }
下面是用树状数组敲的代码:
/* 用树状数组求逆序数:数组A代表数字i是否在序列中出现过, 如果数组i已经存在于序列中,则A[i]=1,否则A[i]=0, 此时Query(i)返回值为在序列中比数字i小的元素的个数, 假设序列中第i个元素的值为a, 那么前i个元素中比i大的元素的个数为i-Query(a). */ #include <cstdio> #include <cstring> #define MAXN 100000 using namespace std; int n,tree[MAXN]; int lowbit(int i) { return i&(-i); } void update(int i,int x) { while(i<=n) { tree[i]=tree[i]+x; i=i+lowbit(i); } } int query(int n) { int sum=0; while(n>0) { sum+=tree[n]; n=n-lowbit(n); } return sum; } int main () { while(scanf("%d",&n)!=EOF) { int a,ans=0; memset(tree,0,sizeof(tree)); for(int i=1;i<=n;i++) { scanf("%d",&a); update(a,1); ans+=i-query(a); } printf("%d\n",ans); } return 0; }