求逆序对的两种方法

归并排序与树状数组求逆序对
本文详细介绍了使用归并排序和树状数组两种方法求解逆序对的过程。归并排序利用其稳定的特性,在合并过程中判断逆序对数量;树状数组则通过对数组进行离散化处理,利用插入和查询操作高效统计逆序对。

归并排序

归排求逆序对用到了二分的思想
设当前归并排序讲合并的区间为[l,r][l,r][l,r]
由于归排二分,所以[l,mid][l,mid][l,mid][mid+1,r][mid+1,r][mid+1,r]两段区间都分别已经排好了序
在排序过程中,如果a[i]>a[j]a[i]>a[j]a[i]>a[j],那么a[i+1]a[i+1]a[i+1]a[mid]a[mid]a[mid]都会大于a[j]a[j]a[j]
即会产生mid−i+1mid-i+1midi+1个逆序对


code

O3 inline void msort(ll l,ll r)
{
	if (l==r)return;
	ll mid=(l+r)>>1,i=l,j=mid+1,k=l;
	msort(l,mid),msort(mid+1,r);
	while (i<=mid && j<=r)
	{
		if (a[i]<=a[j])b[k++]=a[i++];
		else b[k++]=a[j++],ans+=mid-i+1;
	}
	while (i<=mid)b[k++]=a[i++];
	while (j<=r)b[k++]=a[j++];
	fo(i,l,r)a[i]=b[i];
}

归排常数小,经常可以代替手打快排


树状数组

树状数组做法会归排难理解一些
首先对于原数组,需要离散化,尽量不要用快排下标编号因为容易出锅
对于一个数所在位置xxx的插入和查询,我们可以知道在xxx前比这个数小的的数有几个
于是当前插入的数减去xxx位置的答案就是当前位置的逆序对个数
注意插入可正序可逆序,有不同的理解
当然插入和查询完全可以用线段树之类的来实现,但树状数组简单很多


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 500005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))

using namespace std;

ll a[MAXN],b[MAXN],c[MAXN];
ll n,ans;

O3 inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
O3 inline ll lowbit(ll x)
{
	return x&(-x);
}
O3 inline void insert(ll x,ll y)
{
	while (x<=n)c[x]+=y,x+=lowbit(x);
}
O3 inline ll get(ll x)
{
	ll y=0;
	while (x)y+=c[x],x-=lowbit(x);
	return y;
}
O3 inline void init()
{
	sort(b+1,b+n+1),unique(b+1,b+n+1)-b-1;
	fo(i,1,n)a[i]=lower_bound(b+1,b+n+1,a[i])-b;
}
O3 int main()
{
	//freopen("readin.txt","r",stdin);
	n=read();
	fo(i,1,n)a[i]=b[i]=read();
	init();
	fo(i,1,n)
	{
		insert(a[i],1);
		ans+=i-get(a[i]);
	}
	printf("%lld\n",ans);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值