主要摘自逆序数 - 王陸 - 博客园 (cnblogs.com)

逆序对

定义:

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数

举一个例子 2 4 3 1 5

4 之前有0个

3 之前有1个

1 之前有3个

5 之前有0个

所以逆序数就是1+3=4

求法

1,归并排序时间复杂度

归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序中的合并过程中计算逆序数.

2,树状数组

由于树状数组的特性,求和是从当前节点往前求,所以,这里要查询插入当前数值之时,要统计有多少个小于该数值的数还没插入,这些没插入的数,都会在后面插入,也就形成了逆序数。

假设我们将 序列 6 1 2 7 3 4 8 5 存入数组a【】 中, a【1】=6 , a【2】=1...

那么每次,我们可以将 a【i】 插入到 树状数组中,并赋值为 1, 我们求和sum,sum 是1 到 a【i】的和 , 那么这个 sum 表示的值就是当前比a【i】小的数量(包括它本身);而当前一共有 i 个数 , 所以 当前 比a【i】大的数量就是 i - sum;所以 我们统计所有的 i - sum , 它们的和就是逆序数。

int M=1e6;
int a[M],c[M];//a[i]中存的是M个数
int ans=0;
int getsum(int x);//查询x之前有多少个小于等于x的数
void update(int x); //将x标记为出现过
int main()
{
    int cnt;
    for(int i=1;i<=n;i++)
    {
        update(a[i]);
        cnt=getsum(a[i]);
        ans+=i-cnt;
    }
    cout<<ans;
}
int lowbit(int x)//返回的是2^k
{
    return x&(-x);
}
void update(int x, long long d) //将第x个数加上d
{
	while (x <= n)//n为数组的边界
	{
		c[x] ++;
		x += lowbit(x);
	}
}
int getsum(int x)
{
    int t=0;
    while(x>=1)
    {
        t+=c[x];
        x-=lowbit(x);
    }
    return t;
}



posted on 2021-11-10 22:26  naiji  阅读(79)  评论(0)    收藏  举报