POJ2299Ultra-QuickSort (线段树和归并排序的解法)
题目大意就是说帮你给一些(n个)乱序的数,让你求冒泡排序需要交换数的次数(n<=500000)
此题最初真不会做,我也只是在听了章爷的讲解后才慢慢明白过来的
首先介绍线段树的解法:
我们先将原数组每个值附上一个序号index,再将它排序。如题目的例子:
num: 9 1 0 5 4
index: 1 2 3 4 5
排序后:
num: 0 1 4 5 9
index: 3 2 5 4 1
然后由于排序后num为0的点排在原来数组的第3个,所以为了将它排到第一个去,那就至少需要向前移动两次,同时它也等价于最小的数0之前有2个数比它大(所以要移动两次),将0移到它自己的位置后,我们将0删掉(目的是为了不对后面产生影响)。再看第二大的数1,它出现在原数组的第二个,他之前有一个数比它大所以需要移动一次。这样一直循环下去那么着5个数所需要移动的次数就是:
num: 0 1 4 5 9
次数 2 1 2 1 0
将次数全部要加起来就是最后所需要移动的总次数。
方法章爷是已经告诉我了,但是最初我一直是觉得不好实现。到后来才慢慢、慢慢弄好。方法就是在建一棵树时,不是直接将原来的num放进树里面,而是将它的下标放进树里面,最初每个节点上赋值为1.然后当查找第一个num时,由于是找的下标为3的位置,所以我们就直接找区间[1,3)之间有多少个1(就是求前导和),这里面1的个数就是第一个num=0索要移动的次数,然后我们把0去掉,其实也就是吧下标为3的那个1去掉。这样每个值就依次计算出来了。
当然其实只要是想明白了,不用线段树,直接用树状数组写起来会简便很多。(因为每次只需要计算前导和以及去掉某一个点,是对点的操作)。
这里再讲一下归并排序的方法(对于最基础就没有掌握好的我来说听到他们说归并排序可以解题时,我竟然一团雾水,竟然连归并排序都忘记了),看了一下归并排序的实现过程,其实马上就可以找到思路,由于本题实际上就是要求逆序对(即满足i<j,a[i]>a[j]的数对)的个数。而我们再回顾一下归并排序的过程:
假设回溯到某一步,后面的两部分已经排好序(就是说当前需要归并的两个部分都是分别有序的),假设这两个序列为
序列a1:2 3 5 9
序列a2:1 4 6 8
此时我们的目的就是要将a1和a2合并为一个序列。
由于在没排序前a2序列一定全部都是在a1序列之后的,当我们比较a2的1与a1的2时,发现1<2按照归并的思想就会先记录下a2的1,而这里实际上就是对冒泡排序的优化,冒泡是将a2的1依次与a1的9,5,3,2交换就需要4次,而归并却只有一次就完成了,要怎么去记录这个4呢,实际上由于1比2小而2后面还有4个数,也就是说那我的结果就必须要+4,也就是记录a1序列找到第一个比a2某一个大的数,他后面还余下的数的个数就是要交换的次数。
同时我们看a2的4时,a1中第一个比它大的数是5,5之后共有两个数,那结果就+2,。依次下去就可以计算出结果。但是由于我们任然没有改变归并排序的过程。所以复杂度还是O(nlogn),比上面的线段树要快。
另外,此题有一坑就是结果会超int32,,用__int64
首先是线段树的解法11892 KB 1047 ms
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define mem(a) memset(a,0,sizeof(a))
#define MIN(a , b) ((a) < (b) ? (a) : (b))
#define MAXN 500010
#define INF 1000000007
#define lson k<<1, l, mid
#define rson (k<<1)|1, mid+1, r using namespace std; int Tree[MAXN<<], index[MAXN], num[MAXN];
int N; int cmp(const int i, const int j)
{
return num[i] < num[j];
} void Edit(int k, int l, int r, int num, int value)
{
Tree[k] += value;
if(r==l) return ;
int mid = (l+r) >> ;
if(num <= mid) Edit(lson, num, value);
else Edit(rson, num, value);
} int Search(int k, int l, int r, int L, int R)//L,R是要找的区间
{
if(L<=l && r<=R) return Tree[k];
int mid = (l+r) >> ;
int ans = ;
if(L <= mid) ans += Search(lson, L, R);
if(R > mid) ans += Search(rson, L, R);
return ans;
} int main()
{
while(~scanf("%d", &N) && N)
{
mem(Tree); mem(num); mem(index);
for(int i=;i<=N;i++)
{
scanf("%d", &num[i]);
Edit(, , N, i, );
index[i] = i;
}
sort(index+, index+N+,cmp);
long long ans = ;
for(int i=;i<=N;i++)
{
ans += (Search(, , N, , index[i])-);
Edit(, , N, index[i], -);
}
printf("%I64d\n", ans);
}
return ;
}
然后是归并排序(只有注释位置有改动,其他的无变化)4076K 438MS
#include <stdio.h>
#include <string.h>
#define mem(a) memset(a, 0, sizeof(a)) int N, A[], T[];
__int64 ans; void Merg_Sort(int x,int y)
{
if(y-x<=) return ;
int mid = x + (y-x)/;
Merg_Sort(x,mid);
Merg_Sort(mid,y);
int p = x, q = mid, i=x;
while(p<mid || q<y)
{
if(q>=y || (p<mid && A[p] <= A[q])) T[i++] = A[p++];
else//else的条件是(p==mid || A[q] < A[p])
{
if(p<mid) ans+=(mid-p);//由于是p<mid,所以此时也就是相当于 A[q] < A[p]
T[i++] = A[q++]; //上面同时A[p]是第一个<A[q]的数,所以+后面还有的数(mid-p)
}
}
for(i=x;i<y;i++)
{
A[i] = T[i];
}
} int main()
{
while(~scanf("%d", &N) && N)
{
mem(A); mem(T);
for(int i=;i<N;i++)
{
scanf("%d", &A[i]);
}
ans = ;
Merg_Sort(,N);
printf("%I64d\n",ans);//结果会超int32
}
return ;
}
POJ2299Ultra-QuickSort (线段树和归并排序的解法)的更多相关文章
- HDU 6318.Swaps and Inversions-求逆序对-线段树 or 归并排序 or 离散化+树状数组 (2018 Multi-University Training Contest 2 1010)
6318.Swaps and Inversions 这个题就是找逆序对,然后逆序对数*min(x,y)就可以了. 官方题解:注意到逆序对=交换相邻需要交换的次数,那么输出 逆序对个数 即可. 求逆序对 ...
- HDU1394 Minimum Inversion Number(线段树OR归并排序)
Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java ...
- 求逆序数的方法--线段树法&归并排序法
逆序数的概念:对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆 ...
- 主席树/线段树模拟归并排序+二分答案(好题)——hdu多校第4场08
用主席树写起来跑的快一点,而且也很傻比,二分答案,即二分那个半径就行 主席树求的是区间<=k的个数 #include<bits/stdc++.h> using namespace s ...
- [计蒜客T2238]礼物_线段树_归并排序_概率期望
礼物 题目大意: 数据范围: 题解: 这题有意思啊($md$卡常 直接做怎么做? 随便上个什么东西,维护一下矩阵乘和插入,比如说常数还算小的$KD-Tree$(反正我是没见人过过 我们漏掉了一个条件, ...
- POJ 1804 Brainman(5种解法,好题,【暴力】,【归并排序】,【线段树单点更新】,【树状数组】,【平衡树】)
Brainman Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 10575 Accepted: 5489 Descrip ...
- POJ 2299 Ultra-QuickSort 逆序数 树状数组 归并排序 线段树
题目链接:http://poj.org/problem?id=2299 求逆序数的经典题,求逆序数可用树状数组,归并排序,线段树求解,本文给出树状数组,归并排序,线段树的解法. 归并排序: #incl ...
- 线段树菜鸟一题+归并排序【求逆序数】POJ2299
题目链接:http://poj.org/problem?id=2299 归并排序解法链接:http://blog.csdn.net/lyy289065406/article/details/66473 ...
- HDU 1394 Minimum Inversion Number(最小逆序数/暴力 线段树 树状数组 归并排序)
题目链接: 传送门 Minimum Inversion Number Time Limit: 1000MS Memory Limit: 32768 K Description The inve ...
随机推荐
- ajax连接数据库并操作数据库
Response.Write("<script type='text/javascript' language='javascript' >alert('用户名不能为空!请输入 ...
- HDU5427
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> us ...
- Mysql事物与Metadata lock 问题
环境说明: MySQL 5.6.16 OS:Linux RedHat 6.2 64bit 1.问题描述 目前新上一个使用MySQL数据库项目,在数据库中,每隔5分钟做truncate某 ...
- 收缩Oracle数据文件
最近有网友提到收缩Oracle数据文件的问题,这是DBA经常碰到的一个常见问题.通常我们需要收缩相应的数据文件以减少来自磁盘空间的压力以及提高数据库的整体性能.但这并非对于所有情形都是适用的,尤其是生 ...
- Android View绘制13问13答
1.View的绘制流程分几步,从哪开始?哪个过程结束以后能看到view? 答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程.draw ...
- ASP.NET MVC之文件上传【一】
前言 这一节我们来讲讲在MVC中如何进行文件的上传,我们逐步深入,一起来看看. Upload File(一) 我们在默认创建的项目中的Home控制器下添加如下: public ActionResult ...
- 14、NFC技术:使用Android Beam技术传输文本
Android Beam的基本理念 Android Beam的基本理念就是两部(只能是两部)NFC设备靠近时(一般是背靠背),通过触摸一部NFC设备的屏幕,将数据推向另外一部NFC设备.在传递数据的过 ...
- delphi 注册表操作(读取、添加、删除、修改)完全手册
DELPHI VS PASCAL(87) 32位Delphi程序中可利用TRegistry对象来存取注册表文件中的信息. 一.创建和释放TRegistry对象 1.创建TRegistry对象.为了操 ...
- Getting and installing the PEAR package manager
Windows After you have downloaded and installed PHP, you have to manually execute the batch file loc ...
- 【LeetCode】12 & 13 - Integer to Roman & Roman to Integer
12 - Integer to Roman Given an integer, convert it to a roman numeral. Input is guaranteed to be wit ...