逆序对
题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 a i > a j a_i>a_j ai>aj 且 i < j i<j i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
Update:数据已加强。
输入格式
第一行,一个数 n n n,表示序列中有 n n n个数。
第二行 n n n 个数,表示给定的序列。序列中每个数字不超过 1 0 9 10^9 109。
输出格式
输出序列中逆序对的数目。
样例 #1
样例输入 #1
6
5 4 2 6 3 1
样例输出 #1
11
提示
对于 25 % 25\% 25% 的数据, n ≤ 2500 n \leq 2500 n≤2500
对于 50 % 50\% 50% 的数据, n ≤ 4 × 1 0 4 n \leq 4 \times 10^4 n≤4×104。
对于所有数据, n ≤ 5 × 1 0 5 n \leq 5 \times 10^5 n≤5×105
请使用较快的输入输出
应该不会 O ( n 2 ) O(n^2) O(n2) 过 50 万吧 by chen_zhe
思路
此题我用的树状数组,不懂的可以戳此链接树状数组
- 序列的当前位置之后的数如果比当前位置小,那么就会构成逆序对,比如5,4 ,3序列,第一个位置是5,第一个位置之后比5小的有4,3,所以构成了两对逆序对,枚举每一个位置,我们可以将先输入的数标记起来,如果后面输进来的数比标记的数小则就构成了逆序对,也就是在标记的数中找比后输进来的数大的个数,此时我们可以想到修改点+区间查询(树状数组)。
- 修改点就是标记点,区间查询就是查询比此位置的数大的在标记的点中有多少个。
- 注意数据,因为序列中每个数字不超过 1 0 9 10^9 109,数组开不了这么大,所以我们要离散化,至于怎么离散化,不懂的可以去了解一下。
- 另外要注意的是排序的时候,如果值一样,就不要交换位置了,交换了位置也就是交换了输入顺序,会增加情况的,不理解的可以输入有相同值的情况试试。
下面看代码吧!建议将排序后的数据输出来理解,不理解为什么离散化,可以输入一些大的值去理解。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
int t[N];
struct node{
int val,ind;
}stu[N];
int Rank[N]; //存储排序之后的位置,因为数太大了,所以要进行离散化
int n;
int lowbit(int x){return x&(-x);};
int cmp(node a,node b)
{
if(a.val==b.val) //值相等的时候位置不能变
return a.ind<b.ind;
return a.val<b.val;
}
void add(int pos) //修改点
{
for(int i=pos;i<=n;i+=lowbit(i)) t[i]+=1;
}
int search(int L,int R) //求区间和
{
int s=0;
for(int i=L;i;i-=lowbit(i)) s-=t[i];
for(int i=R;i;i-=lowbit(i)) s+=t[i];
return s;
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>stu[i].val;
stu[i].ind=i;
}
sort(stu+1,stu+n+1,cmp);
for(int i=1;i<=n;i++) //离散化
{
Rank[stu[i].ind]=i;
}
int ans=0;
for(int i=1;i<=n;i++)
{
int pos=Rank[i];
ans+=search(pos,n);
add(pos);
}
cout<<ans;
return 0;
}
/*
输入:
6
5 4 2 6 3 1
输出:
11
*/