逆序对数使用归并排序更好,在此我们介绍一种树状数组的实现。
假设一个数组A[n],当A[n]=0时表示数字n在序列中没有出现过,A[n]=1表示数字n在序列中出现过。A对应的树状数组为c[n],则c[n]维护的是数组A[n]前缀和,即树状数组c可用于求A中某个区间的值的和
所以要求序列中比元素a大的数的个数,可以用i - ask(a)即可( i 表示此时序列中元素的个数)。
序列的变化(下划线为新增加元素) | 序列中大于新增加的数字的个数 | 操作 |
{ } | 0 | 初始化时序列中一个数都没有 |
{4 } | 0 | 往序列中增加4,统计此时序列中大于4的元素个数 |
{4 3 } | 1 | 往序列中增加3,统计此时序列中大于3的元素个数 |
{4 3 2} | 2 | 往序列中增加2,统计此时序列中大于2的元素个数 |
{4 3 2 1} | 3 | 往序列中增加1,统计此时序列中大于1的元素个数 |
楼兰图腾tyvj1432 ,用left\right数组 储存每个数的左右正逆序个数,可以省时间
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include <ctime>
#include "queue"
using namespace std;
#define SIZE 200005
#define SIZES 200020
int a[SIZE];//a为目标序列
int tree[SIZES]; //SIZES记录了a最大值大
unsigned long long ans1,ans2;
int n;
int maxn;
int left1[SIZES],right1[SIZES];
int left2[SIZES],right2[SIZES];
int ask(int x)
{
int ans = 0;
for (; x ; x -= x&-x )
ans += tree[x];
return ans;
}
void add(int x, int a)
{
for ( ; x <= maxn ; x += x&-x )
tree[x] += a;
}
int main(int argc, char const *argv[])
{
scanf("%d",&n);
for (int i = 1; i <=n; ++i)
{
scanf("%d",&a[i]);
maxn=max(maxn,a[i]);
}
int i;
memset(tree,0,sizeof(tree));
for ( i = n; i >= 1 ; --i)
{
right1[i] = ask(a[i]);
add(a[i],1);
}
memset(tree,0,sizeof(tree));
for ( i = 1; i <= n ; ++i)
{
left1[i] = ask(a[i]);
add(a[i],1);
}
memset(tree,0,sizeof(tree));
for ( i = n; i >=1 ; --i)
{
right2[i] = ask(maxn) - ask(a[i]);
add(a[i],1);
}
memset(tree,0,sizeof(tree));
for ( i = 1; i <= n ; ++i)
{
left2[i] = ask(maxn) - ask(a[i]);
add(a[i],1);
}
for (i= 2; i <n ; ++i)
{
ans1 += left1[i]*right1[i]; //^的个数
ans2 += left2[i]*right2[i]; //V的个数
}
printf("%llu %llu\n",ans2,ans1);
return 0;
}