题目大意:
1 求一组数字的逆序数对的个数;
2 逆序对:i>j的时候,a[i]<a[j],这样就算一个逆序数对;
解题思路1:暴力枚举(n平方)
1 对于每个 i,询问 j ,j 属于[1,i-1],只要a[j]>a[i],则有一对逆序数对;
暴力代码(25分):
#include<cstdio>
int n,ans=0;
int a[50005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(a[j]>a[i]) ans++;
}
}
printf("%d",ans);
return 0;
}
解题思路2:在归并排序的过程完成统计(nlogn)(先了解归并排序的基础)
1 归并的意思:在递归的"归"的时候,进行合并;
2 设当前“归”的状态是:
2.1 因为左边和右边本来已经有序,所以只要考虑左右合并的问题;
2.2 对于 2 而言,左边的全部 3 个数字都比他大,所以 ans+=3,统计完成之后,进行正常的合并操作;
2.3 对于 5 而言,左边的 6和7比他大,所以ans+=2,统计完之后,进行正常的合并操作;
2.4 对于 8 而言,左边并没有比他大的数字,所以直接进行正常的合并操作;
上代码:
#include<bits/stdc++.h>
int n,a[500005],b[500005];
long long ans=0;
void px(int l,int r)
{
if(l==r) return ;
int mid=(l+r)/2;
px(l,mid); px(mid+1,r);
//以上是二分的思维:递
//以下是归来的时候:合并
int x=l,y=mid+1,t=l;
//x,y分别是两边的游标,t是复制到b数组的游标
//合并的过程:
while(x<=mid&&y<=r)
{
if(a[y]<a[x]) //右边的 y小于左边的 x
{
ans+=mid-x+1;//计数:左边x之后的数字,都大于y
b[t++]=a[y++];
}
else b[t++]=a[x++];//左边的比较小,直接合并
}
//剩余一边的合并(另一边已经结束了):
while(x<=mid) b[t++]=a[x++];
while(y<=r) b[t++]=a[y++];
//复制回原数组
for(int i=l;i<=r;i++) a[i]=b[i];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
px(1,n);//归并排序:过程中进行了计数
printf("%lld",ans);
return 0;
}
解题思路3:树状数组(或者线段树)
1 用桶的思维:当前是第 i 个数字,值是 a[i],可知:在 (a[i],INF) 区间的桶里,有多少个数字,则新增这么多个逆序对;
2 如果用树状数组来进行统计“桶”的前缀和,就搞定了时间复杂度的问题;
3 再机上排序和离散化,就解决了间距的问题;
上代码:
#include<bits/stdc++.h>
using namespace std;
int n,rk[500005],tr[500005];
long long ans=0;
struct nod{int x,i;}a[500005];
bool cmp(nod x,nod y)//值是第一关键字,序号是第二关键字
{
if(x.x==y.x) return x.i<y.i;
return x.x<y.x;
}
int lowb(int x) { return x&-x; }
void ins(int x)//第x大的值
{
while(x<=n)
{
tr[x]++;
x+=lowb(x);
}
}
int su(int x)//前缀和
{
int s=0;
while(x>0)
{
s+=tr[x];
x-=lowb(x);
}
return s;
}
int main()
{
scanf("%d",&n);
memset(tr,0,sizeof(tr));
for(int i=1;i<=n;i++) scanf("%d",&a[i].x),a[i].i=i;
sort(a+1,a+1+n,cmp);//双关键字排序
for(int i=1;i<=n;i++) rk[a[i].i]=i;//获取新排名
for(int i=1;i<=n;i++)
{
ins(rk[i]);//进树
ans+=i-su(rk[i]);//当前树里有i个点,减去比i小的点,就是逆序对的数量;
}
printf("%lld",ans);
return 0;
}