poj2299Ultra-QuickSort【树状数组求逆序数、离散化】、【归并排序模板】

来源:互联网 发布:手机电脑屏幕同步软件 编辑:程序博客网 时间:2024/05/16 15:34

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 ,

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

59105431230

Sample Output

60

Source

Waterloo local 2005.02.05

搁浅了好久了树状数组,尤其是在两天都被操作系统虐的不行不行的情况下,捡起本来就不咋熟练的这货还是有些费事。

这次的专题没做裸的树状数组,逆序数算是树状数组里面比较典型的应用了,尤其是500,000的数据量需要用到离散化,啥是离散化?

以这个题为例,手写了一下中间变量的输出:原数组是9,1,0,5,4  离散后变成了5,2,1,4,3大大缩减了空间开销,这里需要着重注意一下~~

再就说道求逆序数的原理:查询函数返回在序列中比这个数小的个数,假定序列中第i个数为a,那么前i个数中比i大的元素个数为i-sum(i)。当然了,求之前需要调用add函数,函数的第一个参数本来是位置,在这个题中当然带进去的也是离散后的相对位置,貌似说的不是特别明白==

用最原始的树状数组来说明,数组A代表数字i在序列中是否出现过,如果已经存在,那么A【i】=1,否则等于0,,此时查询函数返回值为序列中比数字i小的个数。因为是一位一位add进去,增加一个查询一个,出现了一个数x,则a[x]=1,暂时没出现的就是0,查询的是当前这个数以前的,那么答案显而易见啦

/***************2016.2.1poj22778152K391MSG++1129B***************/#include <iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define maxn 500005int n;int c[maxn],b[maxn];struct Node{    int index,v;}node[maxn];bool cmp(Node a,Node b){    return a.v<b.v;}int lowbit(int x){    return x&(-x);}void add(int i,int val){    while(i<=n)    {        c[i]+=val;        i+=lowbit(i);    }}int sum(int i){    int s=0;    while(i>0)    {        s+=c[i];        i-=lowbit(i);    }    return s;}int main(){    //freopen("cin.txt","r",stdin);    while(~scanf("%d",&n)&&n)    {        for(int i=1;i<=n;i++)        {            scanf("%d",&node[i].v);            node[i].index=i;        }        memset(b,0,sizeof(b));        memset(c,0,sizeof(c));        sort(node+1,node+1+n,cmp);        b[node[1].index]=1;        for(int i=2;i<=n;i++)        {            b[node[i].index]=i;        }        long long ans=0;        for(int i=1;i<=n;i++)        {            add(b[i],1);            ans+=(i-sum(b[i]));        }        printf("%I64d\n",ans);    }    return 0;}


不是自己的代码,但是是裸的应用,没必要再写一遍是吧



#include <stdio.h>#include<string.h>#include<iostream>using namespace std;const int MAXN=500010;long long ans;//存放逆序数,比较大,必须用long longint a[MAXN],b[MAXN],c[MAXN];//将已经排好序的left~mid,mid+1~right进行归并void merge(int *a,int left,int mid,int right){    int i,j;    i=0;    for(j=left;j<=mid;j++)       b[i++]=a[j];    int len1=mid-left+1;    i=0;    for(j=mid+1;j<=right;j++)       c[i++]=a[j];    int len2=right-mid;    i=0;    j=0;    int k=left;    while(i<len1&&j<len2&&k<=right)    {        if(b[i]<=c[j])        {            a[k++]=b[i++];        }        else        {            a[k++]=c[j++];            ans+=(len1-i);//逆序数就是累加后面比自己小的数的个数            //此时b[i]>c[j],那么c[j]会给b[i]后面的len1-i个数造成逆序数        }    }    while(i<len1) a[k++]=b[i++];    while(j<len2) a[k++]=c[j++];}void merge_sort(int *a,int left,int right)//对a[left~right-1]进行归并排序{    if(left<right)    {        int mid=(left+right)/2;        merge_sort(a,left,mid);        merge_sort(a,mid+1,right);        merge(a,left,mid,right);    }}int main(){    //freopen("in.txt","r",stdin);    //freopen("out.txt","w",stdout);    int n;    while(scanf("%d",&n),n)    {        for(int i=0;i<n;i++)          scanf("%d",&a[i]);        ans=0;        merge_sort(a,0,n-1);        printf("%I64d\n",ans);    }    return 0;}



















0 0