poj 2229 Ultra-QuickSort (归并排序求逆序数对|| 树状数组)

http://poj.org/problem?id=2299

Ultra-QuickSort
Time Limit: 7000MS   Memory Limit: 65536K
Total Submissions: 48444   Accepted: 17684

Description

poj 2229 Ultra-QuickSort (归并排序求逆序数对|| 树状数组)_第1张图片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.

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<=500000)个数的杂乱序列,问:如果用冒泡排序,把这n个数排成升序,需要交换几次?

解题思路:

  根据冒泡排序的特点,我们可知,本题只需要统计每一个数的逆序数(如果有i<j,存在a[i] > a[j],则称a[i]与

a[j]为逆序数对),输出所有的数的逆序数的和用普通排序一定会超时,但是比较快的排序,像快排又无法统计

交换次数,这里就很好地体现了归并排序的优点。典型的利用归并排序求逆序数。

  归并排序:比如现在有一个序列[l,r),我们可以把这个序列分成两个序列[l,mid),[mid,r),利用递归按照上

述方法逐步缩小序列,先使子序列有序,再使子序列区间有序,然后再把有序区间合并,很好滴体现了分治的思想。害羞

归并

///#pragma comment (linker, "/STACK:102400000,102400000")

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <queue>
#include <stack>
#include <vector>
#include <map>

using namespace std;

#define N 501005
#define INF 0xfffffff
#define PI acos (-1.0)
#define EPS 1e-8

long long cnt;
int a[N], b[N];
void Merge (int l, int r);

int main ()
{
    int n;
    while (scanf ("%d", &n), n)
    {
        memset (a, 0, sizeof (a));
        memset (b, 0, sizeof (b));

        for (int i=0; i<n; i++)
            scanf ("%d", &a[i]);
        cnt = 0;//一定要用int64,int32会溢出
        Merge (0, n);
        printf ("%I64d\n", cnt);
    }
    return 0;
}

void Merge (int l, int r)//归并排序,参数分别是子区间的位置
{
    if (r - l <= 1) return;
    int mid = (l + r) / 2;
    Merge (l, mid);
    Merge (mid, r);
    int x = l, y = mid, i = l;

    while (x<mid || y<r)//对子序列进行排序,并且存到数组b里面
    {
        if (y>=r || (x<mid && a[x] <= a[y]))
            b[i++] = a[x++];
        else
        {
            if (x < mid)
                cnt += mid - x;//记录交换次数
            b[i++] = a[y++];
        }
    }
    for (int i=l; i<r; i++)//把排好序的子序列抄到a数组对应的位置
        a[i] = b[i];
}

树状数组

用树状数组

序列3  5  4  8  2  6  9

大体思路为:新建一个数组,将数组中每个元素置0

0  0  0  0  0  0  0

取数列中最大的元素,将该元素所在位置置1   统计该位置前放置元素的个数,为0

0  0  0  0  0  0  1

接着放第二大元素8,将第四个位置置1         统计该位置前放置元素的个数,为0

0  0  0  1  0  0  1

继续放第三大元素6,将第六个位置置1        统计该位置前放置元素的个数,为1

0  0  0  1  0  1  1

这样直到把最小元素放完,累加每次放元素是该元素前边已放元素的个数,这样就算出总的逆序数来了在统计和计算每次放某个元素时,该元素前边已放元素的个数时如果一个一个地数,那么一趟复杂度为O(n),总共操

作n趟,复杂度为O(n^2),然而复杂度太大,

当然,在每次统计的过程中用树状数组可以把每一趟计数个数的复杂度降为O(logn),这样整个复杂度就变为O(nlogn)

树状数组是一种很好的数据结构,这有一篇专门描述树状数组的文章

将序列中的每个数按照从大到小的顺序插入到树状数组中,给当前插入节点及其到根节点之间的这条路径上的每个数加1,然后统计该节点右边放置元素的个数


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <queue>
#include <stack>
#include <vector>
#include <map>

using namespace std;
typedef long long LL;

#define N 500110
#define INF 0x3f3f3f3f
#define PI acos (-1.0)
#define EPS 1e-8
#define met(a, b) memset (a, b, sizeof (a))

struct node
{
    int a, id;
}p[N];

bool cmp (node a, node b)
{
    return a.a < b.a;
}

int X[N], tree[N], n;

int lowbit (int t)
{
    return t&(-t);
}

void update (int pos, int num)
{
    while (pos <= n)
    {
        tree[pos] += num;
        pos += lowbit (pos);
    }
}

LL Plus (int pos)
{
    LL ans = 0;
    while (pos)
    {
        ans += tree[pos];
        pos -= lowbit (pos);
    }
    return ans;
}

int main ()
{
    while (scanf ("%d", &n), n)
    {
        for (int i=1; i<=n; i++)
        {
            scanf ("%d", &p[i].a);
            p[i].id = i;
        }

        met (X, 0), met (tree, 0);

        sort (p+1, p+n+1, cmp);

        for (int i=1; i<=n; i++)
            X[p[i].id] = i;

        LL sum = 0;

        for (int i=1; i<=n; i++)
        {
            update (X[i], 1);
            sum += i-Plus (X[i]);
        }
        printf ("%lld\n", sum);
    }
    return 0;
}



你可能感兴趣的:(poj 2229 Ultra-QuickSort (归并排序求逆序数对|| 树状数组))