UVA-10810 Ultra-QuickSort 树状数组+离散化 / 归并排序

Problem B: Ultra-QuickSort

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.

Sample Input

5
9
1
0
5
4
3
1
2
3
0

Output for Sample Input

6
0  
  该题题意是给定一个数组,求出这个数组的逆序对,题目很经典。写了三个不同的算法,第一个是两重循环写法,地球人都知道的。第二种写法就是立神首先写出来的树状数组加离散了,
由于题中最多只有500,000个数据,直接将数据附加一个进入时间的标记,然后按照数据的大小进行排序,这也就是一个离散化的过程,数据中最小的数就华丽变身为下标1,最大的数变身为
N,中间的数依次赋值。最后在扫描一次数组,根据它的原先记录的顺序把离散化之后的值存储起来,比如 9 1 0 5 4 就会变成 5 2 1 4 3 了,再求这个序列的逆序对。这里又用到了树状数
组了,每次插入一个数,询问一次在它后面的数有多少个(实际上是计算前面有多少个,再拿当前个数减去比它小的数就可以了)。
  代码如下:
 
 1 #include <cstdlib>
2 #include <cstring>
3 #include <cstdio>
4 #include <algorithm>
5 using namespace std;
6
7 struct Node
8 {
9 int pos, val;
10 inline bool operator < ( const Node &t ) const
11 {
12 return val < t.val;
13 }
14 }e[500005];
15
16 int rec[500005], sum[500005];
17
18 inline int lowbit( int x )
19 {
20 return x & -x;
21 }
22
23 inline void update( int x, int N )
24 {
25 while( x <= N )
26 {
27 sum[x]++;
28 x += lowbit( x );
29 }
30 }
31
32 inline int ss( int x )
33 {
34 int ans = 0;
35 while( x >= 1 )
36 {
37 ans += sum[x];
38 x -= lowbit( x );
39 }
40 return ans;
41 }
42
43 int main()
44 {
45 int N, M = 500004;
46 while( scanf( "%d", &N ), N )
47 {
48 long long ans = 0;
49 memset( sum, 0 , sizeof( sum ) );
50 M = N;
51 for( int i = 1; i <= N; ++i )
52 {
53 scanf( "%d", &e[i].val );
54 e[i].pos = i;
55 }
56 sort( e + 1, e + N + 1 );
57 for( int i = 1; i <= N; ++i )
58 {
59 rec[e[i].pos] = i;
60 }
61 for( int i = 1; i <= N; ++i )
62 {
63 update( rec[i], N );
64 ans += i - ss( rec[i] );
65 }
66 printf( "%lld\n", ans );
67 }
68 }

 

  第二种写法就是归并排序来进行计算了,首先明确一个观点,就是求某个区间针对一个数的逆序对时,这个区间内的数的排列如何是没有影响的。这为分治法提供了依据,因为如果这个某个区间 a[ i ... j ] 已经有序的话,那么就很好办了,如果一个数 a[N] 比后面的一个数 a[M]大的话,那么逆序对数就直接是区间长度 j - i + 1 减去 N前面的数的总数 N - i + 1。 归并算法就能够很好的解决这个问题。

  代码如下:

 1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #define MAX 500001
5 using namespace std;
6
7 int a[MAX], b[MAX];
8
9 long long ans;
10
11 void merge( int l, int m, int r )
12 {
13 int p = 0, i = l, j = m + 1;
14 while( i <= m && j <= r )
15 {
16 if( a[i] > a[j] )
17 {
18 b[p++] = a[j++];
19 ans += m - i + 1;
20 }
21 else
22 b[p++] = a[i++];
23 }
24 while( i <= m ) b[p++] = a[i++];
25 while( j <= r ) b[p++] = a[j++];
26 for( int i = 0; i < p; ++i )
27 a[l+i] = b[i];
28 }
29
30
31 void Mergesort( int l, int r )
32 {
33 if( l < r )
34 {
35 int m = ( l + r ) / 2;
36 Mergesort( l, m );
37 Mergesort( m + 1, r );
38 merge( l, m, r );
39 }
40 }
41
42
43 int main( )
44 {
45 int N;
46 while( scanf( "%d", &N ), N )
47 {
48 ans = 0;
49 for( int i = 0; i < N; ++i )
50 scanf( "%d", &a[i] );
51 Mergesort( 0, N - 1 );
52 printf( "%lld\n", ans );
53 }
54 return 0;
55 }
posted @ 2011-09-06 14:50  沐阳  阅读(617)  评论(0编辑  收藏  举报