题目大意就是说帮你给一些(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. win7x64安装wince6

    Windows Embedded CE 安装方法 Wince的安装相对比较复杂,即使是一个Wince的老手,也可能遇到这样那样的问题.想来真是悲摧,Windows XP, Windows 7,64位, ...

  2. (转)iOS动画Core Animation

    文章转载:http://blog.sina.com.cn/s/blog_7b9d64af0101b8nh.html 在iOS中动画实现技术主要是:Core Animation. Core Animat ...

  3. POJ 2395 Out of Hay 草荒 (MST,Kruscal,最小瓶颈树)

    题意:Bessie要从牧场1到达各大牧场去,他从不关心他要走多远,他只关心他的水袋够不够水,他可以在任意牧场补给水,问他走完各大牧场,最多的一次需要多少带多少单位的水? 思路:其实就是要让所带的水尽量 ...

  4. (转)每天一个Linux命令(8): tar

    通过SSH访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候tar命令就是是必不可少的一个功能强大的工具.linux中最流行的tar是麻雀虽小,五脏俱全,功能强大. tar命令可以为linux ...

  5. (六)6.6 Neurons Networks PCA

    主成分分析(PCA)是一种经典的降维算法,基于基变换,数据原来位于标准坐标基下,将其投影到前k个最大特征值对应的特征向量所组成的基上,使得数据在新基各个维度有最大的方差,且在新基的各个维度上数据是不相 ...

  6. 【英语】Bingo口语笔记(56) - “令人失望”的表达

  7. Markdown解决需要输入两个回车才能为一个空行的问题

    markdownDataDiv.children().each(function(){ $(this).html($(this).html().replaceAll("\n",&q ...

  8. reStructuredText

    reStructuredText 简明教程 060724 17:58 作者: Laurence 邮箱: 2999am@gmail.com ID: Kardinal @ Ubuntu.org.cn论坛 ...

  9. Oracle数据库“Specified cast is农田valid”

    这种错误是笔者在执行一条计算符合条件的行有多少个,用OracleDataReader读取计算出的行数时发生. 查询语句为: Select Count(1) FROM HP_TS Where TS_ID ...

  10. Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File

    作为一个完成的应用程序,数据存储操作是必不可少的.因此,Android系统一共提供了四种数据存储方式.分别 是:SharePreference.SQLite.Content Provider和File ...