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

题意 : 给出一个数n(n<500,000), 再给出n个数的序列 a1、a2.....an每一个ai的范围是 0~999,999,999  要求出当通过相邻两项交换的方法进行升序排序时需要交换的次数

 

分析 : 经典题目, 只要求出这堆乱序序列里面有多少个逆序对即可, 但是数据量很大, 单纯的循环暴力求法肯定是超时的! 利用树状数组可以高效求解, 代码如下, 建议先百度了解一下

int ans = 0;
for(int i=1; i<=n; i++){
    ans += i - sum(a[i]);//i为已近插入的点的个数
    add(a[i], 1);//单点更新+1操作
}

但是, 这道题有个非常棘手的问题, 那就是每一个ai点的最大可能取值很大,  而树状数组的下标正是来存储这些点的, 很显然数组不可能开那么大,  即对于这题来讲有些数据本身很大, 自身无法作为数组的下标保存对应的属性。但是如果这时只是需要这堆数据的相对属性, 那么可以对其进行离散化处理!

离散化:当数据只与它们之间的相对大小有关,而与具体是多少无关时,可以进行离散化。

因为题目解法只关心个个元素的大小关系, 所以可以采用离散化来优化, 核心也就是在相对大小关系不变的情况下将本来很大的数变成很小的数, 这样就能存进树状数组中进行操作了!

下面针对这道题说明如何进行离散化, 首先举个例子来说明离散化前和离散化后元素都变成了什么样子, 以n = 4为例, Order为给出顺序, val为给出的原始值, new val为离散化后的值

Order            a1          a2      a3      a4

val            9999999    25      100   10000

new val         4            1        2        3

这样变化的话就能将数压到很小, 最大也不过是n的最大取值500000, 作为数组下标来说完全可以接受!下面说做法 :

1、用一个定义了两个整形变量ord和val的结构体数组arr存储原始输入元素的输入顺序和元素的值。

2、再根据结构体val的值进行升序排序, 于是将结构题里面的ord也进行了重排。

3、刚刚说到要刷新的是val, 于是再定义一个数组MAP将1到n的值作为new val去重新刷新val, 具体就是for(int i=1; i<=n; i++) MAP[arr[i].ord] = i;

值得注意的是, 在进行离散化的时候, 高清各个变量和下表等参加离散化操作的关系, 理清楚之后再进行编码, 否则很容易搞混或者搞乱!刚刚上面的循环我就写成了

for(int i=1; i<=n; i++) MAP[i] = arr[i].ord;    然后纳闷了好久为何WA  =_=

#include<stdio.h>
#include<string.h>
#include<string>
#include<map>
#include<set>
#include<algorithm>
#include<iostream>
#include<sstream>
#include<list>
#include<vector>
#include<queue>
#define lowbit(i) (i&(-i))
using namespace std;
const int maxn = 500005;
int c[maxn];
typedef struct ARRAY{
    int ord, val;
}A;
A arr[maxn];
int MAP[maxn];
inline void add(int i, int val)
{
    while(i<500001){
        c[i] += val;
        i += lowbit(i);
    }
}
inline int sum(int i)
{
    int sum = 0;
    while(i>0){
        sum += c[i];
        i -= lowbit(i);
    }
    return sum;
}
bool cmp(const A fir, const A sec)
{
    return fir.val < sec.val;
}
int main(void)
{
    int n;
    while(~scanf("%d", &n) && n){
        memset(c, 0, sizeof(c));
        for(int i=0; i<n; i++){
            scanf("%d", &arr[i].val);
            arr[i].ord = i;
        }
        sort(arr, arr+n, cmp);
        for(int i=0; i<n; i++){
            MAP[arr[i].ord] = i;
        }
        long long cnt = 0;
        for(int i=0; i<n; i++){
            cnt += i - sum(MAP[i]+1);
            add(MAP[i]+1, 1);
        }
        printf("%lld\n", cnt);
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/LiHior/p/6885542.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值