Ultra-QuickSort
Time Limit: 7000MS | Memory Limit: 65536K | |
Total Submissions: 41569 | Accepted: 15067 |
Description
![](https://i-blog.csdnimg.cn/blog_migrate/b3530eaa8fa98ef394d3639722240a8c.jpeg)
Ultra-QuickSort produces the output
Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Input
The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.
Output
For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
Sample Input
5
9
1
0
5
4
3
1
2
3
0
Sample Output
6
0
Source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define ll long long
ll inf = INT_MAX;
ll s[500010]; //存储输入数据,后来存储归并排序后的数据
ll s1[250010]; //临时数组1
ll s2[250010];//临时数组2
ll sum; //统计逆序数
void gbpx(int l,int r,int mid) //mid 把数组分成两部分
{
int lt = 0,rt = 0;
int i,j,k;
for(i = l;i <= mid; i++) //把左边的数放到临时数组1
{
s1[lt++] = s[i];
}
for(j = mid + 1;j <= r; j++)//把右边的数放到临时数组2里
{
s2[rt++] = s[j];
}
s1[lt] = inf;
s2[rt] = inf;//把最后一个设成最大值,为了防止当一个数组归并完后越界
for(i = 0,j = 0,k = l;k <= r; k++)
{
if(s1[i] <= s2[j])//把小的先放进原来的数组
{
s[k] = s1[i++];
}
else
{
s[k] = s2[j++];
sum+=(lt - i);//统计逆序数,在当前数前面的都是比这个数大的,统计起来;
}
}
}
void dy(int ss,int tt)//递归不断的分成两份,直到 ss == tt
{
if(ss < tt)
{
int mid = (ss + tt) >> 1;
dy(ss,mid);
dy(mid + 1,tt);
gbpx(ss,tt,mid);
}
}
int main()
{
int n,i;
while(scanf("%d",&n),n)
{
sum = 0;
for(i = 0;i < n; i++)
{
scanf("%lld",&s[i]);
}
dy(0,n-1);
printf("%lld\n",sum);
}
return 0;
}
线段树代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define ll long long
int d[500010 << 2];//线段树数组
struct node
{
int num;//输入的数
int obh;//一开始的序号
int nbh;//去重后的编号
}ls[500010];//输入数据数组
ll sum;//统计逆序数
bool cmp1(node a,node b)//第一次按照输入的数排序
{
return a.num < b.num;
}
bool cmp2(node a,node b)//第二次按照输入的数排序
{
return a.obh < b.obh;
}
void creat(int s,int t,int step)//线段树创建
{
d[step] = 0;//初始化为0
if(s >= t)//当遇到s == t的时候返回 s > t 是非法数据,为防止死递归
{
return ;
}
int mid = (s + t) >> 1;//求中间值
creat(s,mid,step << 1);//递归左子树
creat(mid + 1,t,step << 1 | 1);//递归右子树
}
void upd(int s,int t,int us,int step)//更新数据
{
if(s > us || t < us)//树下标越界返回
return ;
if(s >= t)//找到s == t的情况,此时,离散化后某个数字对应的编号+1;
{
d[step]++;
return ;
}
int mid = (s + t) >> 1;
if(us <= mid)//us在s到 mid之中的情况
upd(s,mid,us,step << 1);//递归遍历左子树
if(us > mid)//us在mid到t之间的情况
upd(mid + 1,t,us,step << 1 | 1);//递归遍历右子树
d[step] = d[step << 1] + d[step << 1 | 1];//回溯统计求和
}
void qu(int s,int t,int qs,int qt,int step)//询问
{
if(s > qt || t < qs)//树不合法下标
return ;
if(qs <= s && t <= qt)
{
sum += (ll)d[step];//找到某个区间,统计这个区间的逆序数
// printf("sum = %lld\n",sum);
return ;
}
int mid = (s + t) >> 1;//求中间数
if(qs <= mid)
qu(s,mid,qs,qt,step << 1);//递归访问左子树
if(qt > mid)
qu(mid + 1,t,qs,qt,step << 1 | 1);//递归访问右子树
}
int main()
{
int s,t,step,n,us,qs,qt;//s为头,t为尾,step为树下标,n是输入数的个数
//us更新要找的数,qs,qt是询问的区间
while(scanf("%d",&n),n)
{
sum = 0;
for(int i = 0;i < n;i++)
{
scanf("%d",&ls[i].num);
ls[i].obh = i;//初始编号
}
std::sort(ls,ls + n,cmp1);//按照输入的数排序
ls[0].nbh = 1;
for(int i = 1;i < n;i++)//去重,有序后给有序的数组编号
{
if(ls[i].num == ls[i - 1].num)
ls[i].nbh = ls[i - 1].nbh;
else
ls[i].nbh = ls[i - 1].nbh + 1;
}
t = ls[n - 1].nbh;//树的上边界
s = 1;//树的下边界
step = 1;//树的开始下标
creat(s,t,step);//建树
std::sort(ls,ls + n,cmp2);//编完号后再排序回到原来
for(int i = 0;i < n;i++)
{
us = ls[i].nbh;
upd(s,t,us,step);//更新某个编号的映射
qs = ls[i].nbh + 1;
qt = t;//询问这个数后面有几个比他大的,为了统计逆序数的个数
qu(s,t,qs,qt,step);//询问
}
printf("%lld\n",sum);//输出统计的逆序数的个数
}
return 0;
}