POJ 2299 Ultra-QuickSort (树状数组 && 离散化&&逆序)
题意 : 给出一个数n(n<500,000), 再给出n个数的序列 a1、a2.....an每一个ai的范围是 0~999,999,999 要求出当通过相邻两项交换的方法进行升序排序时需要交换的次数
分析:其实经过一次模拟后,会发现奇妙的东西,这个排序都是按位置排的,最大要求到最大,最小要去到最小,转化思想这是一道求逆序对数的题目,答案就是逆序对数。
这里数据过大999999999,数组无法开的了这么大,我们可以离散化,只记录相对大小。
这里离散化有所不同,这里为了压时,用了空间换时间的方法. 前面的文章有讲到sum(i)表示前面有多少比这个小的数,可以sum(n)-sum(i), 这里有新知识就是sum(i)表示有多少小的数,那当前的总数是i,那大的数就是(i-sum(i)这也是求逆序对的方法,目测挺快的。
- .解释为什么要有离散的这么一个过程?
- 刚开始以为999..999这么一个数字,对于int存储类型来说是足够了。
- 还有只有500000个数字,何必要离散化呢?
- 刚开始一直想不通,后来明白了,后面在运用树状数组操作的时候,
- 用到的树状数组C[i]是建立在一个有点像位存储的数组的基础之上的,
- 不是单纯的建立在输入数组之上。
- 比如输入一个9 ,那么C[i]树状数组的建立是在,
- 数据: p[i].val
- 编号: p[i].oder = i*************
- sort
- 数据:
- 编号:
- 顺序:
- a[p[i].编号] = 顺序号;**********************
- a[] = <--;
- a[] = <--;
- a[] = <--;
- a[] = <--;
- a[] = <--;
- a[]={ }
- 新号:
- 值 :
- 下标
- 数组
- 现在由于999999999这个数字相对于500000这个数字来说是很大的,
- 所以如果用数组位存储的话,那么需要999999999的空间来存储输入的数据。
- 这样是很浪费空间的,题目也是不允许的,所以这里想通过离散化操作,
- 使得离散化的结果可以更加的密集。
- 简言之就是开一个大小为这些数的最大值的树状数组
- . 怎么对这个输入的数组进行离散操作?
- 离散化是一种常用的技巧,有时数据范围太大,可以用来放缩到我们能处理的范围;
- 因为其中需排序的数的范围0--- ;显然数组不肯能这么大;
- 而N的最大范围是500 ;故给出的数一定可以与1.。。。N建立一个一一映射;
- ()当然用map可以建立,效率可能低点;
- ()这里用一个结构体
- struct Node
- {
- int val,pos;
- }p[];和一个数组a[];
- 其中val就是原输入的值,pos是下标;
- 然后对结构体按val从小到大排序;
- 此时,val和结构体的下标就是一个一一对应关系,
- 而且满足原来的大小关系;
- for(i=;i<=N;i++)
- a[p[i].pos]=i;
- 然后a数组就存储了原来所有的大小信息;
- 比如 ------- 离散后aa数组
- 就是 ;
- 具体的过程可以自己用笔写写就好了。
- . 离散之后,怎么使用离散后的结果数组来进行树状数组操作,计算出逆序数?
- 如果数据不是很大, 可以一个个插入到树状数组中,
- 每插入一个数, 统计比他小的数的个数,
- 对应的逆序为 i- sum( a[i] ),
- 其中 i 为当前已经插入的数的个数,
- sum( a[i] )为比 a[i] 小的数的个数,
- i- sum( a[i] ) 即比 a[i] 大的个数, 即逆序的个数
- 但如果数据比较大,就必须采用离散化方法
- 假设输入的数组是9 , 离散后的结果a[] = {,,,,};
- 在离散结果中间结果的基础上,那么其计算逆序数的过程是这么一个过程。
- .输入5, 调用add(, ),把第5位设置为1
- 计算1-5上比5小的数字存在么? 这里用到了树状数组的sum() = 1操作,
- 现在用输入的下标1 -sum() = 就可以得到对于5的逆序数为0。
- . 输入2, 调用add(, ),把第2位设置为1
- 计算1-2上比2小的数字存在么? 这里用到了树状数组的sum() = 1操作,
- 现在用输入的下标2 - sum() = 就可以得到对于2的逆序数为1。
- . 输入1, 调用add(, ),把第1位设置为1
- 计算1-1上比1小的数字存在么? 这里用到了树状数组的sum() = 1操作,
- 现在用输入的下标 -sum() = 就可以得到对于1的逆序数为2。
- . 输入4, 调用add(, ),把第5位设置为1
- 计算1-4上比4小的数字存在么? 这里用到了树状数组的sum() = 3操作,
- 现在用输入的下标4 - sum() = 就可以得到对于4的逆序数为1。
- . 输入3, 调用add(, ),把第3位设置为1
- 计算1-3上比3小的数字存在么? 这里用到了树状数组的sum() = 3操作,
- 现在用输入的下标5 - sum() = 就可以得到对于3的逆序数为2。
- . ++++ = 这就是最后的逆序数
- 分析一下时间复杂度,首先用到快速排序,时间复杂度为O(NlogN),
- 后面是循环插入每一个数字,每次插入一个数字,分别调用一次add()和sum()
- 外循环N, add()和sum()时间O(logN) => 时间复杂度还是O(NlogN)
- #include <iostream>
- #include <cstdio>
- #include <algorithm>
- using namespace std;
- typedef long long ll;
- const int N=5e5+;
- struct node{
- int val;
- int pos;
- }p[N];
- int n,bit[N],a[N];
- bool cmp(const node&a, const node& b){
- return a.val<b.val;
- }
- void add(int i){
- while(i<=n){
- bit[i]+=;
- i+=i&-i;
- }
- }
- int sum(int i){
- int s=;
- while(i>){
- s+=bit[i];
- i-=i&-i;
- }
- return s;
- }
- void solve(){
- for(int i=; i<=n; i++){
- scanf("%d",&p[i].val);
- p[i].pos=i;
- }
- sort(p+,p+n+,cmp);//排序
- for(int i=; i<=n; i++)a[p[i].pos]=i;//离散化
- ll ans=;
- for (int i=; i<=n; i++) bit[i]=; //初始化树状数组
- for(int i=; i<=n; i++){
- add(a[i]);
- ans+=i-sum(a[i]);
- }
- printf("%I64d\n",ans);
- }
- int main(){
- while(~scanf("%d",&n)&&n){
- solve();
- }
- return ;
- }
POJ 2299 Ultra-QuickSort (树状数组 && 离散化&&逆序)的更多相关文章
- poj 2299 Ultra-QuickSort(树状数组求逆序数+离散化)
题目链接:http://poj.org/problem?id=2299 Description In this problem, you have to analyze a particular so ...
- poj 2299 Ultra-QuickSort(树状数组求逆序数)
链接:http://poj.org/problem?id=2299 题意:给出n个数,求将这n个数从小到大排序,求使用快排的需要交换的次数. 分析:由快排的性质很容易发现,只需要求每个数的逆序数累加起 ...
- POJ 2299 Ultra-QuickSort(树状数组+离散化)
http://poj.org/problem?id=2299 题意:给出一组数,求逆序对. 思路: 这道题可以用树状数组解决,但是在此之前,需要对数据进行一下预处理. 这道题目的数据可以大到999,9 ...
- POJ - 2299 Ultra-QuickSort 【树状数组+离散化】
题目链接 http://poj.org/problem?id=2299 题意 给出一个序列 求出 这个序列要排成有序序列 至少要经过多少次交换 思路 求逆序对的过程 但是因为数据范围比较大 到 999 ...
- POJ 2299 Ultra-QuickSort【树状数组 ,逆序数】
题意:给出一组数,然后求它的逆序数 先把这组数离散化,大概就是编上号的意思--- 然后利用树状数组求出每个数前面有多少个数比它小,再通过这个数的位置,就可以求出前面有多少个数比它大了 这一篇讲得很详细 ...
- Ultra-QuickSort(树状数组求逆序对数)
Ultra-QuickSort 题目链接:http://poj.org/problem?id=2299 Time Limit: 7000MS Memory Limit: 65536K Total ...
- hdu2838树状数组解逆序
离散化和排序后的序号问题搞得我实在是头痛 不过树状数组解逆序和偏序一类问题真的好用 更新:hdu的数据弱的真实,我交上去错的代价也对了.. 下面的代码是错的 /* 每个点的贡献度=权值*在这个点之前的 ...
- [zoj4046][树状数组求逆序(强化版)]
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4046 题意:有一个含有n个元素的数列p,每个元素均不同且为1~n中的一个, ...
- poj 2299 Ultra-QuickSort(树状数组)
Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 67681 Accepted: 25345 ...
随机推荐
- java判断姓是否合格 百家姓
package util; import java.lang.reflect.Array; public class FirstName { public static boolean ClearNa ...
- Android开源地图项目 BigPlanetTracks 学习随笔
一. app主体部分 [tyt.android.bigplanettracks] 二. 地图部分 [tyt.android.bigplanettracks.maps] ...
- JavaScript中常用的函数
javascript函数一共可分为五类: ·常规函数 ·数组函数 ·日期函数 ·数学函数 ·字符串函数 1.常规函数 javascript常规函数包括以下9个函数: (1)alert函数 ...
- clearfix的用法(转)
clearfix的用法 (2013-12-31 10:41:24) 标签: clearfix 清除浮动 clearboth height zoom 分类: 网页制作 如果有一个DIV作为外部容器,内部 ...
- 百度Apollo解析——2.log系统
Apollo中的glog 在Apollo中google glog 被广泛使用,glog 是 google 的一个 c++ 开源日志系统,轻巧灵活,入门简单,而且功能也比较完善. 1. 安装 以下是官方 ...
- SpringBoot06 统一响应格式
1 要求 每个请求成功后,后台返回的响应格式都是一致的,例如: 2 创建一个视图模型 该模型用于格式化响应数据 package cn.xiangxu.springboottest.model.data ...
- hihocoder1513 小Hi的烦恼
传送门 分析 论bitset的妙用......我们利用桶排将输入的数据排序,之后分别考虑5维,a[i][j]表示考虑第i个人第j维的情况下于其它人的大小关系.最后将5维的信息并起来求1的个数即可 代码 ...
- 914D Bash and a Tough Math Puzzle
传送门 分析 用线段树维护区间gcd,每次查询找到第一个不是x倍数的点,如果这之后还有gcd不能被x整除的区间则这个区间不合法 代码 #include<iostream> #include ...
- HTML完全使用详解 PDF扫描版
<HTML完全使用详解>根据网页制作的实际特点和目前市场需要,全面系统地介绍了最新的HTML4.01.丰富的实例贯穿全书,能帮助您全面掌握HTML,而且本书所有实例均可直接修改使用,可以提 ...
- 无废话MVC入门教程笔记
自学mvc,看了园子里李林峰写的李林峰写的无废话MVC入门教程笔记,现在有的平时忽略的或是不太清楚的点记下来 1,Html.DropDownList //服务端写法 @{ //下拉列表的值 List& ...