随笔 - 73  文章 - 0 评论 - 0 阅读 - 6680
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

主要摘自逆序数 - 王陸 - 博客园 (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   naiji  阅读(54)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示