题目链接:点击查看

题目大意:给出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(线段树+离散化/归并排序)相关推荐

  1. POJ Mayor's posters——线段树+离散化

    原文:http://blog.163.com/cuiqiongjie@126/blog/static/85642734201261151553308/ 大致题意: 有一面墙,被等分为1QW份,一份的宽 ...

  2. 【POJ 2482】 Stars in Your Window(线段树+离散化+扫描线)

    [POJ 2482] Stars in Your Window(线段树+离散化+扫描线) Time Limit: 1000MS   Memory Limit: 65536K Total Submiss ...

  3. poj 2528 Mayor's posters(线段树+离散化)

    1 /* 2 poj 2528 Mayor's posters 3 线段树 + 离散化 4 5 离散化的理解: 6 给你一系列的正整数, 例如 1, 4 , 100, 1000000000, 如果利用 ...

  4. poj2528贴海报(线段树离散化)

    //poj2528贴海报(线段树离散化) #include<cstring> #include<iostream> #include<cstdio> #includ ...

  5. HDOJ 2492 Ping pong 线段树+离散化

    //2492 Ping pong 线段树+离散化 /* 题意: 有一陀人从左到右排成一排,每个人有一个唯一的技能值,每个人都找其他人比赛, 比赛前要再找一个人做裁判,裁判的技能值不能比这两个人都高,也 ...

  6. POJ 2299 Ultra-QuickSort(线段树+离散化)

    题目地址:POJ 2299 这题以前用归并排序做过.线段树加上离散化也能够做.一般线段树的话会超时. 这题的数字最大到10^10次方,显然太大,可是能够利用下标,下标总共仅仅有50w.能够从数字大的開 ...

  7. poj 2482 Stars in Your Window(线段树+离散化+线扫描)

    题目:http://poj.org/problem?id=2482 大意:在一个坐标系中给你n(10^4)个点(点的坐标范围为0<=x,y<2^31),每个点有一个权值,然后给你一个长宽分 ...

  8. POJ - 2528 线段树+离散化

    其实很早就在白书上的常用技巧上 看到离散化的操作,但是之前一直没遇到过需要离散化的题目(应该是我太菜的缘故),所以一直也没怎么重视,下面说说这道题目的考点,也就是离散化. 什么是离散化呢?请先自行百度 ...

  9. poj 2528 Mayor's posters (线段树+离散化)

    /*离散化+线段树由于 数据的输入最大是 10000000 ,直接用开数组肯点会超,所以要将起离散话,首先 ,我们存储输入的边,将其离散化,后面的就和一般的线段树一样可. */#include< ...

最新文章

  1. html自定义颜色函数,javascript设置元素背景颜色
  2. python工具安装教程_Python 开发工具PyCharm安装教程图文详解(新手必看)
  3. 基于 Zookeeper 的分布式锁实现
  4. 多线程:保证三个线程依次按顺序执行?newSingleThreadExecutor!!!
  5. Oracle® Database Performance Tuning Guide 11g Release 2 (11.2)----Configuring and Using Memory
  6. C++ 容器的综合应用的一个简单实例——文本查询程序
  7. 5G毫米波三大自然缺陷
  8. [19/06/07-星期五] CSS基础_布局定位背景样式
  9. linux大数据命令操作
  10. Python日志模块logging高级用法
  11. easyui被activeX控件挡住的解决方法
  12. 3给定关键字不在字典中_Python龙珠训练营五:数据结构之字符串、字典
  13. AMOS分析技术:路径分析的非递归模型
  14. PS2022 安装教程
  15. 用Qt图形视图框架开发拼图游戏
  16. MySQL数据库出现unknown error 1449错误原因及其解决办法
  17. 巴比特 | 元宇宙每日必读:在NFT领域卖铲子能有多赚钱?云厂商们认为这就像“发电厂”业务...
  18. VCS(DVE)调试
  19. 电商API数据采集,教你如何获取商品详情数据
  20. 【高等数学如何学,做题方法,期末突击】

热门文章

  1. 初识Sentinel
  2. DockerCompose-部署微服务集群
  3. TransactionTemplate和@Transactional注解的区别
  4. leader选举的源码分析-quorumPeer.createElectionAlgorithm
  5. Elastic-Job中的ScriptJob
  6. 自定义类型转换器代码编写
  7. 单例-单例设计模式代码实现
  8. 通过Zuul上传文件,禁用Zuul的Filters
  9. jvm参数调优堆的初始和最大值一定要一致
  10. SpringBoot整合 ActiveMQ、SpringBoot整合RabbitMQ、SpringBoot整合Kafka