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 ...
随机推荐
- js调试的一点小知识
1.如果想要js代码被XHTML和HTML解析,就可以使用如下方式 <script type="text/javascript"> //<![CDATA[ fun ...
- Emulator PANIC: Could not open: AVD2.3.1
这是这两年的sdk才需要这样,以前这样根本没错的 在环境变量 里面增加一个系统变量ANDROID_SDK_HOME,值就是当前的系统用户文件夹的位置.比如c:\\Users\xxx(不要加.andro ...
- JS继承方式详解
js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念.所以,要想实现 ...
- __get(),__set(),__isset(),__unset()
__get(),__set(),__isset(),__unset() 在给不可访问属性赋值时,__set()会被调用读取不可访问属性的值时,__get()会被调用 当对不可访问属性调用isset() ...
- C++中的一类临时对象
类名(参数名)这样的对象是临时对象,不能取地址,不能被引用,不过可以给同类型的其他对象赋值,该临时对象定以后可以进行一次操作,然后立即销毁. 当我们定义一个对象以后并不想立即给它赋初值,而是以后给它赋 ...
- TaikrSpaceShooterStartKit.unitypackage包下载地址
有好多教程里面没有资源包,现在加密分享给大家 unity4.* 链接: https://pan.baidu.com/s/1XMo2zVpV3ZhkNZKOb6H0yw 密码: tqnt unity5 ...
- USB无线网卡导致耳机电流声很大
今天把许久未用的USB无线网卡插入到电脑中,戴上耳机准备听音乐,发现耳机里面的电流声非常大.回想以前并没有这种状况呀,忽然发现原来是USB无线网卡和耳机都插在前置面板中了,把USB无线网卡插在后置面板 ...
- Be a Smart Raftsman SGU475
传送门 题目大意 有m+1个点,0是起点,m是终点,i-1到i有一条边,有一个船由0驶往m,不能返回,它在载重小于等于ci时通过第i条边消耗的时间为di否则为Di,现在有n个人,每个人体重为wi,上船 ...
- CF 464E The Classic Problem
补一补之前听课时候的题. 考虑使用dij算法求最短路,因为边权存不下,所以考虑用主席树维护二进制位,因为每一次都只会在一个位置进行修改,所以可以暴力进位,这样均摊复杂度是对的. <算法导论> ...
- Java Swing 创建转圈的进度提示框
Java Swing 创建转圈的进度提示框 摘自 https://blog.csdn.net/nihaoqiulinhe/article/details/52439486 置顶2016年09月05日 ...