逆序数 (树状数组+数组离散化)
描述
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。
比如 1 3 2 的逆序数就是1。
输入
第一行输入一个整数T表示测试数据的组数(1<=T<=5)每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000)随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。
输出
输出该数列的逆序数输出该数列的逆序数
分析
这题还是用树状数组来求逆序数,但是输入的数据很多,数组是不能开这么大的。这时就要把数组离散。
举个例子,有四个数99999999 1 123 1583 数据范围太大,而树状数组中的c数组开的范围是数据的范围,这时候就需要离散化,把四个数一次标号为1 2 3 4(即第一个数,第二个数。。。),按键值排序之后 依次为2 3 4 1(即从小到大排序为第二个数,第三个数。。。),所以,第二个数是最小的,即f[2]=1,f[3]=2,f[4]=3,f[1]=4,也就是把键值变为了1~n,相对大小还是不变的,即4 1 2 3。比如要求原来四个数的逆序数总和,现在就是求4 1 2 3的逆序数总和,大大节省了空间压力(树状数组的长度是数据范围)
代码
#include<bits/stdc++.h>
using namespace std;
int e[1000000+50];
///数组的离散化
struct node
{
int id;
int val;
} a[1000000+50];
bool cmp(node a,node b)
{
if(a.val!=b.val)//注意排序方式
return a.val<b.val;
return a.id<b.id;
}
int lowBit(int x)
{
return x&(-x);
}
int SUM(int n)
{
int sum=0;
while(n>0)
{
sum+=e[n];
n-=lowBit(n);//从父节点向下加
}
return sum;
}
void updata(int id,int v,int n)
{
while(id<=n)
{
e[id]+=v;
id+=lowBit(id);//从底部向父节点更新
}
}
int main()
{
//freopen("2.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
long long int sum=0;
memset(a,0,sizeof(a));
memset(e,0,sizeof(e));
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i].val);
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
for(int i=1; i<=n; i++)
{
updata(a[i].id+1,1,n+1);
sum+=i-SUM(a[i].id+1);
}
printf("%lld\n",sum);
}
return 0;
}
梦里不知身是客,一晌贪欢。