【解题思路】
计算过程等同于使用冒泡排序法,将输入的数据序列进行排序的算法,
因此,此问题有另外一个名字:【计算冒泡排序次数】
如果 不考虑数据规模及超时,直接进行排序,每移动一次就累加一次,
最后输出累计的移动次数便是正解。
【方法1:朴素算法】
const int MAXI = 100002;
int cnt[MAXI];
int num[MAXI];
int n,i,j,answer,val;
int main(){
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&num[i]);
cnt[i]=0;
}
answer = 0;
for(i=1;i<=n;i++){
val = num[i];
for(j=val+1;j<=n;j++){
answer += cnt[j];
}
cnt[val]++;
}
printf("%d\n",answer);
return 0;
}
num数组中存放:输入的数据序列
cnt数组中存放:已经处理的每个数据的出现次数(1。。。n上长顺序),
如:cnt[4]=2 是指4在此前的处理中遇到了几2次。
如果当前比较数字是3,那么需要分析比3大的值 cnt[4]...cnt[max]出现的次数,
累加的 次数之和就是逆序对个数:
for(j=val+1;j<=n;j++){
answer += cnt[j];
}
【方法2:树状数组算法】
由于以上方法1的算法是N方的复杂度,题中要求N<100000,
显然会超时,大概只能通过50%用例。
优化方法:计算cnt[i]...cnt[max]出现的次数的算法复杂度从方法1的N次,降低为Log N次
使用数据结构:树状数组 BIT(Binary Indexed Tree)
1、从BIT结构中查询 :每次减去二进制的最后一个1位,直到二进制位为0;
公式:i -= (i & -i)
int get(int i){
int result=0;
while(i>0){
result+=cnt[i];
i -= (i & -i); // Key code
}
return result;
}
2. 更新BIT结构中的值(更新当前值及高位累加值 ) :每次加上二进制的最后一个1位,直到二进制位的值 《=N;
公式:i += (i & -1)
void update(int i,int val){
while (i<=n){
cnt[i]+=val;
i+= (i & -i); // Key code
}
}
3. 完整实现:
#include <cstdio>
#include <iostream>
using namespace std;
const int MAXI = 100002;
int cnt[MAXI];
int num[MAXI];
int n,val;
long long answer;
void update(int i,int val){
while (i<=n){
cnt[i]+=val;
i+= (i & -i);
}
}
int get(int i){
int result=0;
while(i>0){
result+=cnt[i];
i -= (i & -i);
}
return result;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
cnt[i]=0;
}
answer = 0;
for(int i=1;i<=n;i++){
val = num[i];
answer +=1ll*(get(n)-get(val));
update(val,1);
}
printf("%lld\n",answer);
return 0;
}