Ultra-QuickSort(树状数组+逆序数)

传送门 POJ2299

描述

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 ,
Ultra-QuickSort produces the output
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.

输入

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.

输出

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.

样例

  • Input
    5
    9
    1
    0
    5
    4
    3
    1
    2
    3
    0
  • Output
    6
    0

题解

  • 题意:给你一个整型数组,用相邻两个元素两两交换的方式使得整个数列从小到大排列,求出需要交换的次数
  • 题意简单来说就是用冒泡排序需要交换多少次,但是我们都知道冒泡排序的时间复杂度是,暴力模拟肯定是会超时的。通过观察我们不难发现,交换次数的总和其实就是数列的逆序数之和(所谓逆序数就是在该元素之前比他大的元素个数),所以我们求出每一个元素的逆序数然后加起来就可以求出总共的交换次数。
  • 我们首先通过快排求出所有元素应在的位置,用数组t来存放他们的出现状态,然后遍历原来的数组,每一次将每一个元素填入它应在的位置i(t[i]++),然后算出它前面出现过的n个数(包括自身),这n个数都是比它小的,我们用它当前位置减去n就可以知道在原序列中在他前面有多少个数比他大,也就是他的逆序数。
  • 上面的单点修改区间求和这样的方法计算的话时间复杂度依旧是,所以我们使用树状数组进行优化,这样时间复杂度就变成了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#define LL long long int
#define INIT(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define per(i,b,a) for(int i=b-1;i>=a;i--)
//b——0,-1,128,0x3f,127 ,字符
const double Pi = acos(-1);
const double E = exp(1.0);
const LL mod =1e9+7;
const int MAX=0x7fffffff;
const int MIN=-0x7fffffff;
const int INF=0x3f3f3f3f;
using namespace std;
LL n;LL tree[500001];LL t[500001];
struct Node{
LL data;LL num;
bool operator < (Node e) const{return data<e.data;}
}node[500001];
LL lowbit(LL x){
return x & -x;
}
void add(LL x,LL p){
while(x<=n){
tree[x]+=p;
x+=lowbit(x);
}
}
LL sum(LL x){
int tem=0;
while(x>0){
tem+=tree[x];
x-=lowbit(x);
}
return tem;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n&&n){
INIT(tree,0);
for(int i=1;i<=n;i++){
cin>>node[i].data ;
node[i].num =i;
}
sort(node+1,node+n+1);
for(int i=1;i<=n;i++)
t[node[i].num]=i;
LL Sum=0;
for(int i=1;i<=n;i++){
add(t[i],1);
Sum+=(i-sum(t[i]));
}
cout<<Sum<<endl;
}
return 0;
}

PS

当数组元素都属于0~n时,可以直接使用以下方式:

1
2
3
4
5
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add(a[i]+1,1); //+1是防止0造成死循环
ans+=i-sum(a[i]+1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值