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 ...
随机推荐
- 使用MFC中的AfxBeginThread创建多线程
创建一个基于对话框的工程,工程名为CreateThreadRect 在CreateThreadRect.cpp中增加一个ThreadProc函数,代码如下 工作者线程的函数必须是全局函数或静态 ...
- 20160201.CCPP体系详解(0011天)
内容概要:C语言基本数据类型及运算题库(含答案) 第二章 基本数据类型及运算 一.选择题 1. 若以下选项中的变量已正确定义,则正确的赋值语句是[C]. A) x1=26.8%3; B) 1+2=x2 ...
- AIX 第2章 指令记录
root@db:/#mount node mounted mounted over vfs date options ------- ...
- Oracle buffer cache与相关的latch等待事件
buffer cache与相关的latch等待事件 1.buffer cache 2.latch:cache buffers lru chain 3.latch:cache buffers chain ...
- 集合框架null与size=0
被QA人员一眼指出来的问题,唉,好丢人 上栗子
- 【转】不可变数组NSArray与可变数组NSMutableArray
原文网址:http://www.jianshu.com/p/1ad327f56d1d 不可变数组NSArray //创建一个空数组 NSArray *array = [NSArray array]; ...
- 用vs2012的命令利用xsd文件生成对应的C#类,把xml的string类型映射到生成的类
输入命令: xsd d:\TDDOWNLOAD\atom-author-link.xsd /c /language:C# /outputdir:d:\ 含义: 将d:\TDDOWNLOAD\atom- ...
- 【PHP入门到精通】:Ch04:流程控制语句
Ch04: 流程控制语句4.1 条件控制语句(1)if (expr) { statement1;statement2;} (2)if (expr) { statement1; } else { s ...
- hdu 3478(判断奇环)
题意:给你一个无向图,问你有没有可能存在一个奇环连接所有的节点. 分析:好久没写博客了,这个好习惯还是要继续保持的!这道题通过转化之后就是问你有没有存在一个奇环连接所有的节点,这里用到的方法是染色法, ...
- Dubbo原理解析-注册中心之Zookeeper协议注册中心
下面我们来看下开源dubbo推荐的业界成熟的zookeeper做为注册中心, zookeeper是hadoop的一个子项目是分布式系统的可靠协调者,他提供了配置维护,名字服务,分布式同步等服务.对于z ...