归并排序
归排求逆序对用到了二分的思想
设当前归并排序讲合并的区间为[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+1mid−i+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;
}
归并排序与树状数组求逆序对

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

被折叠的 条评论
为什么被折叠?



