题目传送门
Description
In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
9
1
0
5
4
9 \space 1 \space 0 \space 5 \space 4
9 1 0 5 4
Ultra-QuickSort produces the output
0
1
4
5
9
0\space 1\space 4\space 5\space 9
0 1 4 5 9
Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Input
The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 – the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.
Output
For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
Sample Input
5
9
1
0
5
4
3
1
2
3
0
Sample Output
6
0
题意
输入一个数字n,然后输入n个数,求逆序数( n < 500000 , 0 ≤ a [ i ] ≤ 999999999 n < 500000,0 ≤ a[i] ≤ 999999999 n<500000,0≤a[i]≤999999999)
思路
很明显数字是不连续的,需要离散化处理,具体实现是先按照数字大小升序排序,修改每个数字为1-n的数,之后再按输入的顺序顺序排序,比如输入序列为9、1、0、5、4,处理之后是5、2、1、4、3
然后使用树状数组求逆序数即可
我们之前的 n 2 n^2 n2暴力统计是遍历当前数字x前面的数字,统计比x大的数字,那么树状数组是sum(n)- sum(x)(sum表示前缀和),每读入一个数字,就在每个数的位置加1,利用树状数组快速求前缀和,统计逆序数。举个例子:设输入序列为5、2、1、4、3 (n=5,共5个数字),求解过程如下所示:
-
则读入5时,在5的位置加1,表示5出现了一次
数组 0 0 0 0 1
下标 1 2 3 4 5
此时逆序数为0,输入序列中,5前面没有数字
sum(5)- sum(5)= 1-1=0
-
读入2,在2的位置加1
那么树状数组只需要统计 (2,n] 这个区间的和就可以
因为我们是一边读一遍更新数组,2之后的数组区间和就是在2前面出现的,比2大的数字的个数,因为读入一个数字我们就给对应位置加1,所以数组区间和就是数字的个数!
数组 0 1 0 0 1
下标 1 2 3 4 5
用树状数组的就表示为 sum(5)- sum(2)= 2-1=1
此时逆序数为0+1=1
-
读入1,1的位置加1,统计 (1,n] 这个区间的和
数组 1 1 0 0 1
下标 1 2 3 4 5
用树状数组的就表示为 sum(5)- sum(1)= 3-1=2
此时逆序数为0+1+2=3
-
读入4,4的位置加1,同样统计 (4,n] 这个区间的和
数组 1 1 0 1 1
下标 1 2 3 4 5
用树状数组的就表示为 sum(5)- sum(4)= 4-3=1
此时逆序数为0+1+2+1=4
-
读入3,3的位置加1,同样统计 (3,n] 这个区间的和
数组 1 1 1 1 1
下标 1 2 3 4 5
用树状数组的就表示为 sum(5)- sum(3)= 5-3=2
此时逆序数为0+1+2+1+2=6
于是我们就这样求出了逆序数!复杂度为nlogn
树状数组讲解:树状数组 数据结构详解与模板(可能是最详细的了)
代码
//#include <bits/stdc++.h>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#define MAXN 501000
using namespace std;
typedef long long ll ;
int tree[MAXN];
int lowbit(int x){
return (-x)&x;
}
int add(int x,int val){
while (x<MAXN){
tree[x]+=val;
x+=lowbit(x);
}
}
ll sum(int x){
ll res=0;
while (x>0){
res+=tree[x];
x-=lowbit(x);
}
return res;
}
struct node{
int num;
int pos;
}arr[MAXN];
bool cmp(node a,node b){
return a.num<b.num;
}
bool cmp2(node a,node b ){
return a.pos<b.pos;
}
void init(){
memset(tree,0,sizeof(tree));
}
int main (){
int n;
while(scanf("%d",&n)!=EOF){
if (!n)break;
init();
ll ans=0;
for (int i=1;i<=n;i++){
scanf("%d",&arr[i].num);
arr[i].pos=i;
}
sort(arr+1,arr+n+1,cmp);
for (int i=1;i<=n;i++){
arr[i].num=i;
}
sort(arr+1,arr+n+1,cmp2);
for (int i=1;i<=n;i++){
add(arr[i].num,1);
ans+=sum(n)-sum(arr[i].num);
}
printf("%lld\n",ans);
}
return 0;
}