原题题址
其实嘛,这个求冒泡排序的次数的问题,好像叫啥逆序对,我不是很了解啊。我就知道,最小次数就是每个数最小交换次数的和,每个数最小交换次数就是找出是每个数前面有几个大于他的数就结了么。
晓得了这个,介个问题嘛,就按想法整,从前往后找,找在当前数前面有几个大于它的数。虽然是权值线段树的活,但是由于某些原因,我们就用树状数组搞定它。
那咋整呢?
首先,把序列排序(便于单点修改,要是线段树就不需要),按照原序列顺序插入到树状数组中,实际上是对应位置+1,查询区间[1,i]的和,即该区间内数的个数,i - sum(i)就是比a[i]大的数的个数。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 5e5+5;
typedef long long ll;
int N,a[maxn],disc[maxn],sum[maxn];
int lowbit(int x){
return x&-x;
}
void Updata(int p,int v){
while(p <= N){
sum[p]+=v;
p+=lowbit(p);
}
}
ll Query(int p){
int ans = 0;
while(p){
ans+=sum[p];
p-=lowbit(p);
}
return ans;
}
int main(){
while(~scanf("%d",&N)){
if(N==0)break;
memset(sum,0,sizeof(sum));
for(int i = 1;i <= N;i++){
scanf("%d",&a[i]);
disc[i] = a[i];
}
sort(disc + 1,disc + 1 + N);
ll ans = 0;
for(int i = 1;i <= N;i++){
int tmp = lower_bound(disc + 1,disc + 1 + N,a[i]) - disc;
Updata(tmp,1);
ans += i - Query(tmp);
}
printf("%lld\n",ans);
}
return 0;
}