【算法】逆序对

逆序对,简单来说,就是i > j*,但a[i] < a[j],那么a[i] 和 a[j]就是一组逆序对

求逆序对有三种方式——

  • 暴力 复杂度 O(n2) 谁用谁T 不多赘述了
  • 归并排序 复杂度 O(nlogn)
  • 树状数组 复杂度 O(nlogn)

归并排序

首先看一下归并排序的原理,就是将一个序列无限二分,直到每一部分都只有一个元素,这时每一部分都有序,然后逐次合并相邻部分,让合并后的各个部分有序
举个栗子
现在我们要合并两个部分:1 3 5 7 9 | 2 4 6 8 10
先比较 1、2,发现 1 < 2,所以把 1 先放到合并后的数组里
现在剩下的两部分是:3 5 7 9 | 2 4 6 8 10
现在比较3、2,发现 3 > 2,所以把 2 放到合并后的数组,由于左半部分是有序的,所以 2 小于左半部分剩下的所有数,但 2 又在左半边剩下的所有数后面,所以 2 和这些数都构成逆序对,逆序对的数量就是mid - i
每一次右半部分的第一个数小于左半部分的第一个数时,右半部分的第一个数和左半部分剩下的所有数都构成逆序对,因此在原来的基础上加上mid - i即可

代码如下:

#include <bits/stdc++.h>

using namespace std;

const int N = 1010;

int ans;
int a[N], temp[N];

void merge_pai(int l, int r, int mid)
{
	int i = l, j = mid, p = l;
	while (i < mid && j <= r)
	{
		if (a[i] < a[j]) temp[p ++ ] = a[i ++ ];
		else
		{
			temp[p ++ ] = a[j ++ ];
			ans += mid - i;
		}
	}
	while (i < mid) temp[p ++ ] = a[i ++ ];
	while (j <= r) temp[p ++ ] = a[j ++ ];
	p = l;
	while (p < mid) a[p ++ ] = temp[p ++ ];
}

void merge_sort(int l, int r)
{
	if (l < r)
	{
		int mid = l + r >> 1;
		merge_sort(l, mid);
		merge_sort(mid + 1, r);
		merge_pai(l, r, mid + 1);
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int n; cin >> n;
	for (int i = 0; i < n; i ++ ) cin >> a[i];
	merge_sort(1, n);
	cout << ans;
}

树状数组

树状数组的原理在这里就不多说啦,如果有不懂的同学可以去看看这篇

建立一个数组c[i],表示元素 i 出现的次数,起初每一项都为0
同时我们也可以得到c[i]的树状数组tree[i]
每次插入一个数 x ,我们都将利用树状数组单点修改的性质更新tree[i]的值,此时区间查询c[i - 1]的前缀和,我们就可以得到在 x 前输出,比 x 的值小的元素个数,用总共输入的元素个数和它相减,就可以得到在 x 前输出,且比 x 大的元素个数,这也就是逆序对的个数了
代码如下:

#include <bits/stdc++.h>

using namespace std;

const int N = 110;

int tree[N];

int lowbit(int x)
{
	return x & -x;
}

void add(int i, int x)
{
	while (i <= N)
	{
		tree[i] += x;
		i += lowbit(i);
	}
}

int prefix_sum(int i)
{
	int presum = 0;
	while (i > 0)
	{
		presum += tree[i];
		i -= lowbit(i);
	}
	return presum;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int n; cin >> n;
	int a[n + 1];
	int ans = 0;
	for (int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
		add(a[i], 1);
		ans += (i - prefix_sum(a[i]));
	}
	cout << ans;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值