POJ 2299 Ultra-QuickSort (树状数组or 归并排序分治求逆序对数)

题目大意就是说帮你给一些(n个)乱序的数,让你求冒泡排序需要交换数的次数(n<=500000)

显然不能直接模拟冒泡排序,其实交换的次数就是序列的逆序对数。

由于数据范围是 0 ≤ a[i] ≤ 999,999,999所以先要离散化,然后用合适的数据结果求出逆序

可以用线段树一步一步添加a[i],每添加前查询前面添加比它的大的有多少个就可以了。

也可用树状数组,由于树状数组求的是(1...x)的数量和所以每次添加前查询i-sum(a[i])即可

树状数组:

//5620K	688MS
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
#define lowbit(x) (x&-x)
const int M=5e5+100;
int num[M];
int Hash[M];
int tree[M]; //树状数组
int n;
int Bin(int val) //二分出离散后的序号
{
    int l=0,r=n-1;
    while(r>=l){
        int m=(l+r)>>1;
        if(Hash[m]==val) return m+1;
        if(Hash[m]>val) r = m-1;
        else l=m+1;
    }
}
void add(int rt,int x)
{
    while(rt<=M-1){
        tree[rt]+=x;
        rt+=lowbit(rt);
    }
}

ll getsum(int rt)
{
    ll s=0;
    while(rt>0){
        s+=tree[rt];
        rt-=lowbit(rt);
    }
    return s;
}
int main()
{

    while(scanf("%d",&n),n){
        memset(tree,0,sizeof(tree));
        ll ans=0;
        for(int i=0;i<n;i++){
            scanf("%d",&num[i]);
            Hash[i]=num[i];
        }
        sort(Hash,Hash+n);
        for(int i=0;i<n;i++){
            int val=Bin(num[i]);
            ans+=(ll)i-getsum(val);
            add(val,1);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

用分治法求逆序数也可,分成两个数组B,C  。  B中的逆序数+C中的逆序数+对于每一个C[i]B中比它大的个数

所以可以借助归并排序实现顺带算出逆序数

//5428K	625MS
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
#define lowbit(x) (x&-x)
const int M=5e5+100;
int num[M];
int tmp[M];
int Hash[M];

int n;
int Bin(int val) //二分出离散化后的序号
{
    int l=0,r=n-1;
    while(r>=l){
        int m=(l+r)>>1;
        if(Hash[m]==val) return m+1;
        if(Hash[m]>val) r = m-1;
        else l=m+1;
    }
}

ll merge_count(int l,int r)
{
    if(l==r){
        return 0;
    }
    ll cnt=0;
    int m=(l+r)>>1;
    cnt+=merge_count(l,m);
    cnt+=merge_count(m+1,r);
    int a=0,b=l,c=m+1;
    while(a<r-l+1){

        if(b<=m&& (c==r+1 ||num[b]<=num[c])){
            tmp[a++]=num[b++]; // sort
        }
        else{
            tmp[a++]=num[c++];  // sort
            cnt+=m-b+1;
        }

    }
    for(int i=0;i<r-l+1;i++) //还原到原数列
        num[l+i]=tmp[i];
    return cnt;
}

int main()
{

    while(scanf("%d",&n),n){

        for(int i=0;i<n;i++){
            scanf("%d",&num[i]);
            Hash[i]=num[i];
        }
        sort(Hash,Hash+n);
        for(int i=n-1;i>=0;i--){
            int val=Bin(num[i]);
            num[i+1]=val;
            //我离散后的数列设成从1开始到n,因为后面分治怕出错就套用了线段树从结点1开始的写法...2333
        }

        printf("%I64d\n",merge_count(1,n));
    }
    return 0;
}
时间: 2024-08-10 20:07:55

POJ 2299 Ultra-QuickSort (树状数组or 归并排序分治求逆序对数)的相关文章

归并排序,树状数组 两种方法求逆序对

我们知道,求逆序对最典型的方法就是树状数组,可是另一种方法就是Merge_sort(),即归并排序. 实际上归并排序的交换次数就是这个数组的逆序对个数,为什么呢? 我们能够这样考虑: 归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来. 在合并的过程中(设l<=i<=mid,mid+1<=j<=h).当a[i]<=a[j]时.并不产生逆序数:当a[i]>a[j]时.在 前半部分中比a[i]大的数都比a[j]

poj 2299 Ultra-QuickSort 离散化 + 树状数组

题目链接:http://poj.org/problem?id=2299 离散化 + 树状数组 教科书例题般的题目 #include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <cmath> #include <vector> #include <stack> #include <set> #include

poj 2299 Ultra-QuickSort (树状数组+离散化)

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 48257   Accepted: 17610 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin

poj 2299 Ultra-QuickSort(树状数组求逆序数+离散化)

题目链接:http://poj.org/problem?id=2299 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

POJ 2299 Ultra-QuickSort(树状数组+离散化)

http://poj.org/problem?id=2299 题意:给出一组数,求逆序对. 思路: 这道题可以用树状数组解决,但是在此之前,需要对数据进行一下预处理. 这道题目的数据可以大到999,999,999,但数组肯定是无法开这么大的,但是每组数据最多只有500000个,那么,怎么办呢,离散化! 离散化,也就是将数据和1~n做一一映射. 比如: 9 1 0 5 4 离散化之后变成 5 2 1 4 3 这样的话,就可以放心的开数组啦! 至于树状数组的计算过程,我懒得写了,直接摘抄一下大神的h

POJ 2299 Ultra-QuickSort (树状数组)

前段时间用归并排序写了这题,发现树状数组也能解这题,就去学习了一下 首先先来看一个序列   6 1 2 7 3 4 8 5,此序列的逆序数为5+3+1=9.冒泡法可以直接枚举出逆序数,但是时间复杂度太高O(n^2).冒泡排序的原理是枚举每一个数组,然后找出这个数后面有多少个数是小于这个数的,小于它逆序数+1.仔细想一下,如果我们不用枚举这个数后面的所有数,而是直接得到小于这个数的个数,那么效率将会大大提高. 总共有N个数,如何判断第i+1个数到最后一个数之间有多少个数小于第i个数呢?不妨假设有一

Poj 2299 - Ultra-QuickSort 离散化,树状数组,逆序对

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 52306   Accepted: 19194 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin

POJ 2299 Ultra-QuickSort(树状数组)

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 47014   Accepted: 17182 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin

Bestcoder7(1004)hdu4988(经典问题:树状数组套treap求解动态逆序对)

Little Pony and Boast Busters Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 83    Accepted Submission(s): 32 Problem Description "I hereby challenge you, Ponyvillians: anything you can do