POJ - 2299 Ultra-QuickSort(线段树+离散化/归并排序)
题目链接:点击查看
题目大意:给出n个数字,求使用冒泡排序所需要交换的次数
题目分析:这个题n给到了5e5,如果直接冒泡排序的话,的时间复杂度肯定就TLE了,所以不能直接暴力模拟
我们换个思路,这个题其实需要转化一下,就是求每个位置上数字的逆序数,然后加和就是所要求的答案了
因为相对较大的数字经过排序后要往右边移动,而任意位置的一个数字所要移动的距离就是他的逆序数,即需要和每一个他右边
比他小的数字所交换一次,所以现在问题转化为了求这串数字的逆序数了
那么该怎么求逆序数呢?我们就要用到线段树了,我先来大体描述一下该怎么用线段树求逆序数吧:
我们先要知道求一个位置上的逆序数,有两种途径,一种是通过求该位置之前比它本身大的数字的个数,另一种就是通过求该位
置之后比它本身小的数字的个数
我们先假设一个数列为5,1,3,2,4,我们需要求这个数列每一项的逆序数,我们就用第一种方法来求吧,就是求每个位置之前比它
本身大的数字的个数,首先我们先假设下面五个方块依次代表着没一个数字,点亮后的方块代表前面已经遍历过的数字
□ □ □ □ □
一开始还没有进行填充,我们现在开始遍历一边这个数列:
首先第一个数字是5,我们需要查找比5大的数字,即查找区间(5+1,5),很显然这个区间是不存在的,因为左端点大于了右端
点,故第一个数的逆序数为0,在遍历下一个数字之前,需要将第一个数字点亮,即变成这个样子。
□ □ □ □ ■
然后我们再处理第二个数字,我们需要找到比1大的数字,即查找区间(1+1,5)中有多少个点亮的方块,很显然答案是1,然后
更新点亮的状态,将第一个灯也点亮
■ □ □ □ ■
接下来处理第三个数字,是3,那么我们需要查找区间(3+1,5),答案是1,更新状态:
■ □ ■ □ ■
然后是处理数字2,我们需要查找区间(2+1,5),答案是2,更新状态:
■ ■ ■ □ ■
最后只剩一个4了,我们需要查找区间(4+1,5),答案是1,更新状态:
■ ■ ■ ■ ■
到此为止,我们将上述结果加和,得到的便是这一串数列的逆序数了,即0+1+1+2+1=5
然后稍微总结一下,关于上述的两个操作:“查找区间”和“点亮状态”的操作,分别对应着线段树中的区间查找和点更新,是不是有
点感觉了?
等等等等!这个题到这里还没完呢,为什么呢?因为这个题的数据范围,n是5e5,还不算太大,那么每一个位置上的数呢?范围
竟然是999999999?我们都知道线段树是根据范围来开的,如果按照范围的话,我们需要开至少4e10的数组才能实现上述的方
法,不过很显然,会爆内存,那我们该怎么处理呢?
这个题涉及到了二维偏序问题,在这个题目中,每个数字的位置代表一个维度,每个数字的数值代表着另一个维度,因为他的数
值涉及到的范围很广,所以我们需要将其离散化来处理,即在存储每一个数字的时候一起存上他原本的位置,然后对于他的数值
排序,然后对于他的位置来求逆序数,这样就可以将求逆序数的范围规范到了1~n之间了,剩下的就可以用线段树来解决了。
2019.11.29更新:
学习了归并排序的原理和内部实现后,发现这个题目用归并排序能以稳定的nlogn的时间复杂度解决,挂一下代码
上代码:更多的会在代码里注释
线段树:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<sstream>
#include<cmath>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e5+100;struct Node
{int l,r,sum;
}tree[N<<2];struct node
{int pos,val;
}a[N];void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].sum=0;if(l==r){ return;}int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}void add(int k,int pos)
{if(tree[k].l==tree[k].r){tree[k].sum=1;return;}int mid=(tree[k].l+tree[k].r)>>1;if(mid>=pos)add(k<<1,pos);elseadd(k<<1|1,pos);tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}int query(int k,int l,int r)
{if(l>r)return 0;if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].r<=r&&tree[k].l>=l)return tree[k].sum;return query(k<<1,l,r)+query(k<<1|1,l,r);
}bool cmp(node a,node b)
{return a.val<b.val;
}int main()
{
// freopen("input.txt","r",stdin);int n;while(scanf("%d",&n)!=EOF&&n){build(1,1,n);for(int i=1;i<=n;i++){scanf("%d",&a[i].val);a[i].pos=i;}sort(a+1,a+1+n,cmp);//离散化LL ans=0;//注意这里记得开long long,因为计算的过程中会爆int,因为这个WA了一发for(int i=1;i<=n;i++){ans+=query(1,a[i].pos+1,n);//我们求每个数字前比他大的数字的个数add(1,a[i].pos);//每次处理完记得“点亮”这个点}cout<<ans<<endl;}return 0;
}
归并排序:
#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e5+100;int a[N],b[N];LL merge_sort(int l,int r)
{if(l==r)//只有一个元素直接返回return 0;int mid=l+r>>1;LL ans=merge_sort(l,mid)+merge_sort(mid+1,r);//分别往左侧和右侧递归int p=l,q=mid+1;for(int i=l;i<=r;i++)//合并,数组b是辅助数组,辅助排序用的{if(q>r||p<=mid&&a[p]<=a[q])b[i]=a[p++];else{b[i]=a[q++];ans+=mid-p+1;//这里,既然q点属于当前右区间内最小的点了,那么左区间内比他大的点有mid-p+1个}}for(int i=l;i<=r;i++)a[i]=b[i];return ans;
}int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);int n;while(scanf("%d",&n)!=EOF&&n){for(int i=1;i<=n;i++)scanf("%d",a+i);printf("%lld\n",merge_sort(1,n));}return 0;
}
POJ - 2299 Ultra-QuickSort(线段树+离散化/归并排序)相关推荐
- POJ Mayor's posters——线段树+离散化
原文:http://blog.163.com/cuiqiongjie@126/blog/static/85642734201261151553308/ 大致题意: 有一面墙,被等分为1QW份,一份的宽 ...
- 【POJ 2482】 Stars in Your Window(线段树+离散化+扫描线)
[POJ 2482] Stars in Your Window(线段树+离散化+扫描线) Time Limit: 1000MS Memory Limit: 65536K Total Submiss ...
- poj 2528 Mayor's posters(线段树+离散化)
1 /* 2 poj 2528 Mayor's posters 3 线段树 + 离散化 4 5 离散化的理解: 6 给你一系列的正整数, 例如 1, 4 , 100, 1000000000, 如果利用 ...
- poj2528贴海报(线段树离散化)
//poj2528贴海报(线段树离散化) #include<cstring> #include<iostream> #include<cstdio> #includ ...
- HDOJ 2492 Ping pong 线段树+离散化
//2492 Ping pong 线段树+离散化 /* 题意: 有一陀人从左到右排成一排,每个人有一个唯一的技能值,每个人都找其他人比赛, 比赛前要再找一个人做裁判,裁判的技能值不能比这两个人都高,也 ...
- POJ 2299 Ultra-QuickSort(线段树+离散化)
题目地址:POJ 2299 这题以前用归并排序做过.线段树加上离散化也能够做.一般线段树的话会超时. 这题的数字最大到10^10次方,显然太大,可是能够利用下标,下标总共仅仅有50w.能够从数字大的開 ...
- poj 2482 Stars in Your Window(线段树+离散化+线扫描)
题目:http://poj.org/problem?id=2482 大意:在一个坐标系中给你n(10^4)个点(点的坐标范围为0<=x,y<2^31),每个点有一个权值,然后给你一个长宽分 ...
- POJ - 2528 线段树+离散化
其实很早就在白书上的常用技巧上 看到离散化的操作,但是之前一直没遇到过需要离散化的题目(应该是我太菜的缘故),所以一直也没怎么重视,下面说说这道题目的考点,也就是离散化. 什么是离散化呢?请先自行百度 ...
- poj 2528 Mayor's posters (线段树+离散化)
/*离散化+线段树由于 数据的输入最大是 10000000 ,直接用开数组肯点会超,所以要将起离散话,首先 ,我们存储输入的边,将其离散化,后面的就和一般的线段树一样可. */#include< ...
最新文章
- html自定义颜色函数,javascript设置元素背景颜色
- python工具安装教程_Python 开发工具PyCharm安装教程图文详解(新手必看)
- 基于 Zookeeper 的分布式锁实现
- 多线程:保证三个线程依次按顺序执行?newSingleThreadExecutor!!!
- Oracle® Database Performance Tuning Guide 11g Release 2 (11.2)----Configuring and Using Memory
- C++ 容器的综合应用的一个简单实例——文本查询程序
- 5G毫米波三大自然缺陷
- [19/06/07-星期五] CSS基础_布局定位背景样式
- linux大数据命令操作
- Python日志模块logging高级用法
- easyui被activeX控件挡住的解决方法
- 3给定关键字不在字典中_Python龙珠训练营五:数据结构之字符串、字典
- AMOS分析技术:路径分析的非递归模型
- PS2022 安装教程
- 用Qt图形视图框架开发拼图游戏
- MySQL数据库出现unknown error 1449错误原因及其解决办法
- 巴比特 | 元宇宙每日必读:在NFT领域卖铲子能有多赚钱?云厂商们认为这就像“发电厂”业务...
- VCS(DVE)调试
- 电商API数据采集,教你如何获取商品详情数据
- 【高等数学如何学,做题方法,期末突击】