题目大意就是说帮你给一些(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 (线段树和归并排序的解法)的更多相关文章

  1. HDU 6318.Swaps and Inversions-求逆序对-线段树 or 归并排序 or 离散化+树状数组 (2018 Multi-University Training Contest 2 1010)

    6318.Swaps and Inversions 这个题就是找逆序对,然后逆序对数*min(x,y)就可以了. 官方题解:注意到逆序对=交换相邻需要交换的次数,那么输出 逆序对个数 即可. 求逆序对 ...

  2. HDU1394 Minimum Inversion Number(线段树OR归并排序)

    Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java ...

  3. 求逆序数的方法--线段树法&归并排序法

    逆序数的概念:对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆 ...

  4. 主席树/线段树模拟归并排序+二分答案(好题)——hdu多校第4场08

    用主席树写起来跑的快一点,而且也很傻比,二分答案,即二分那个半径就行 主席树求的是区间<=k的个数 #include<bits/stdc++.h> using namespace s ...

  5. [计蒜客T2238]礼物_线段树_归并排序_概率期望

    礼物 题目大意: 数据范围: 题解: 这题有意思啊($md$卡常 直接做怎么做? 随便上个什么东西,维护一下矩阵乘和插入,比如说常数还算小的$KD-Tree$(反正我是没见人过过 我们漏掉了一个条件, ...

  6. POJ 1804 Brainman(5种解法,好题,【暴力】,【归并排序】,【线段树单点更新】,【树状数组】,【平衡树】)

    Brainman Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 10575   Accepted: 5489 Descrip ...

  7. POJ 2299 Ultra-QuickSort 逆序数 树状数组 归并排序 线段树

    题目链接:http://poj.org/problem?id=2299 求逆序数的经典题,求逆序数可用树状数组,归并排序,线段树求解,本文给出树状数组,归并排序,线段树的解法. 归并排序: #incl ...

  8. 线段树菜鸟一题+归并排序【求逆序数】POJ2299

    题目链接:http://poj.org/problem?id=2299 归并排序解法链接:http://blog.csdn.net/lyy289065406/article/details/66473 ...

  9. HDU 1394 Minimum Inversion Number(最小逆序数/暴力 线段树 树状数组 归并排序)

    题目链接: 传送门 Minimum Inversion Number Time Limit: 1000MS     Memory Limit: 32768 K Description The inve ...

随机推荐

  1. UISegment

    UISegment分段控制 属性 1.segmentedControlStyle 设置segment的显示样式. typedef NS_ENUM(NSInteger, UISegmentedContr ...

  2. gcc g++ 参数介绍

    C和C++ 编译器是集成的.他们都要用四个步骤中的一个或多个处理输入文件: 预处理 (preprocessing),编译(compilation),汇编(assembly)和连接(linking).源 ...

  3. web项目路径问题

    路径    相对路径        URL中第一个字符不为“/”        request.getRequestDispatcher("b");        相对于该代码所在 ...

  4. HDU 1023 Train Problem II (卡特兰数,经典)

    题意: 给出一个数字n,假设火车从1~n的顺序分别进站,求有多少种出站序列. 思路: 卡特兰数的经典例子.n<101,用递推式解决.需要使用到大数.n=100时大概有200位以下. #inclu ...

  5. django - 修改 自增长id,起始值

    常常你会遇到这样的情况,需要自增长的起始值是 0,再次从 0开始. 两个选择: 1. drop table_name; django重新建表. 2. ALTER TABLE table_name AU ...

  6. 使用Nodejs+mongodb开发地图瓦片服务器

    原先地图瓦片服务器采用的是arcgisserver发布的地图服务并进行切片,但ags发布的地图服务很占内存,发布太多的话服务器压力很大.再一个就是ags价太高了. 学习Nodejs之后,发现这是一个可 ...

  7. 预热buffer pool

    mysqldump -u root db_name table_name> /dev/null select * from ...

  8. myeclipse 10 优化

    一.Myeclipse10修改字体 MyEclipse10是基于Eclipse3.7内核,但在Eclipse的Preferences-〉general-〉 Appearance->Colors ...

  9. Ubuntu 安装

    最近又有工作需要,又需要在虚拟机上工作了.记得上次使用Ubuntu的时候还是7,8年前呢 用的是vmware 7 ,buntu的版本记不清了.时隔多年又捡起来了,记忆还停留在过去,于是被折腾惨了. 1 ...

  10. 读取raw目录中的文件数据

    try { InputStream is2 = getResources().openRawResource(R.raw.info); InputStreamReader isr2 = new Inp ...