POJ 2299-Ultra-QuickSort-线段树的两种建树方式
此题有两种建树方式!
Description
In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order.
For the input sequence
9 1 0 5 4 ,
Ultra-QuickSort produces the output
0 1 4 5 9 .
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
题目大意:
给定一个排列,求这个排列的逆序数。
逆序的定义
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。
核心思想:
我们用结构体来存储排列:

a[i].x表示第i个数的数值,a[i].id表示第i个数的位置。
对于每一个数a[i].x,我们找到位置在它前面并且数值比它大的数的个数c[i],c数组的和就是答案。
线段树。
我们在统计个数的时候,数要满足两个条件:
1、位置在它前面
2、数值比它大
我们在空间和时间上分别加以控制来满足这两个条件。
空间:线段树的左右区间。
时间:线段树的更新顺序。
所以此题有两种建树方式,
方式一
从空间上加以控制来满足位置在它前面的条件;
从时间上加以控制来满足数值比它大的条件。
线段树的左右区间表示原排列中数的位置,通过控制查询时左右区间的端点来满足位置在它前面的条件。
我们将a数组按x降序排列,按照新的顺序依次更新线段树,数值的大的先进入(更新)线段树,所以现在从线段树上查询到的个数一定满足数值比它大的条件。
此方式建树不需要离散化,下面的方式需要离散化。
方式二
从空间上加以控制来满足数值比它大的条件;
从时间上加以控制来满足位置在它前面的条件。
线段树的左右区间表示原排列中数的数值,通过控制查询时左右区间的端点来满足数值比它大的条件。
我们将a数组按id升序排列(也就是原顺序),位置靠前的先进入(更新)线段树,所以现在从线段树上查询到的个数一定满足位置在它前面的条件。
注意:此题目数的数值范围是1e10,将数值作为线段树区间长度开不出这么大的数组。解决方法是离散化。

离散前后的映射函数是:

离散后的数值范围是不同数值的个数,也就是5e5。这样就可以开数组建树了。
方式一代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=5e5+20;
char s[20];
//存原排列
struct node{
int x,id;
}a[N];
struct tnode{
int l,r,sum;
}tr[N<<2];
void pushup(int m)
{
tr[m].sum=tr[m<<1].sum+tr[m<<1|1].sum;
return;
}
void build(int m,int l,int r)
{
tr[m].l=l;
tr[m].r=r;
if(l==r)
{
tr[m].sum=0;
return;
}
int mid=(l+r)>>1;
build(m<<1,l,mid);
build(m<<1|1,mid+1,r);
pushup(m);
return;
}
void update(int m,int x)
{
if(tr[m].l==x&&tr[m].r==x)
{
tr[m].sum=1;
return;
}
int mid=(tr[m].l+tr[m].r)>>1;
if(x<=mid)
update(m<<1,x);
else
update(m<<1|1,x);
pushup(m);
return;
}
int query(int m,int l,int r)
{
if(tr[m].l==l&&tr[m].r==r)
return tr[m].sum;
int mid=(tr[m].l+tr[m].r)>>1;
if(r<=mid)
return query(m<<1,l,r);
if(l>mid)
return query(m<<1|1,l,r);
return query(m<<1,l,mid)+query(m<<1|1,mid+1,r);
}
bool cmp(node p,node q)
{
return p.x>q.x;
}
int main()
{
int n;
while(1)
{
scanf("%d",&n);
if(!n)
break;
//根据n直接建树
build(1,1,n);
//输入
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].id=i;
}
//排序,x大的先进入线段树
sort(a+1,a+n+1,cmp);
//边更新边查询
ll ans=0;
for(int i=1;i<=n;i++)
{
ans+=query(1,1,a[i].id);
update(1,a[i].id);
}
//输出
printf("%lld\n",ans);
}
return 0;
}
方式二代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=5e5+20;
int b[N],cnt;
//存原排列
struct node{
int x,id;
}a[N];
struct tnode{
int l,r,sum;
}tr[N<<2];
void pushup(int m)
{
tr[m].sum=tr[m<<1].sum+tr[m<<1|1].sum;
return;
}
void build(int m,int l,int r)
{
tr[m].l=l;
tr[m].r=r;
if(l==r)
{
tr[m].sum=0;
return;
}
int mid=(l+r)>>1;
build(m<<1,l,mid);
build(m<<1|1,mid+1,r);
pushup(m);
return;
}
void update(int m,int x)
{
if(tr[m].l==x&&tr[m].r==x)
{
tr[m].sum=1;
return;
}
int mid=(tr[m].l+tr[m].r)>>1;
if(x<=mid)
update(m<<1,x);
else
update(m<<1|1,x);
pushup(m);
return;
}
int query(int m,int l,int r)
{
if(tr[m].l==l&&tr[m].r==r)
return tr[m].sum;
int mid=(tr[m].l+tr[m].r)>>1;
if(r<=mid)
return query(m<<1,l,r);
if(l>mid)
return query(m<<1|1,l,r);
return query(m<<1,l,mid)+query(m<<1|1,mid+1,r);
}
bool cmp1(node p,node q)
{
return p.x<q.x;
}
bool cmp2(node p,node q)
{
return p.id<q.id;
}
int getid(int x)
{
return lower_bound(b,b+cnt,x)-b+1;
}
int main()
{
int n;
while(1)
{
scanf("%d",&n);
if(!n)
break;
//输入
a[0].x=-1;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].id=i;
}
//先离散化
sort(a+1,a+n+1,cmp1);
cnt=0;
for(int i=1;i<=n;i++)
if(a[i].x!=a[i-1].x)
b[cnt++]=a[i].x;
//根据离散后的cnt建树
build(1,1,cnt);
//别忘了把a数组sort回来
sort(a+1,a+n+1,cmp2);
//边更新边查询
ll ans=0;
for(int i=1;i<=n;i++)
{
int t=getid(a[i].x);
ans+=query(1,t,n);
update(1,t);
}
//输出
printf("%lld\n",ans);
}
return 0;
}
POJ 2299-Ultra-QuickSort-线段树的两种建树方式的更多相关文章
- 线段树:HDU2795-Billboard(建树方式比较新奇)
Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...
- POJ 3225 线段树区间更新(两种更新方式)
http://blog.csdn.net/niuox/article/details/9664487 这道题明显是线段树,根据题意可以知道: (用0和1表示是否包含区间,-1表示该区间内既有包含又有不 ...
- CodeForces Round #179 (295A) - Greg and Array 一个线段树做两次用
线段树的区间更新与区间求和...一颗这样的线段树用两次... 先扫描1~k...用线段树统计出每个操作执行的次数... 那么每个操作就变成了 op. l , op.r , op.c= times* ...
- poj 3667 Hotel (线段树的合并操作)
Hotel The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a ...
- 巨蟒python全栈开发数据库前端6:事件onclick的两种绑定方式&&onblur和onfocus事件&&window.onload解释&&小米商城讲解
1.回顾上节内容(JavaScript) 一.JavaScript概述 1.ECMAScript和JavaScript的关系 2.ECMAScript的历史 3.JavaScript是一门前后端都可以 ...
- Web APi之认证(Authentication)两种实现方式【二】(十三)
前言 上一节我们详细讲解了认证及其基本信息,这一节我们通过两种不同方式来实现认证,并且分析如何合理的利用这两种方式,文中涉及到的基础知识,请参看上一篇文中,就不再叙述废话. 序言 对于所谓的认证说到底 ...
- Android中BroadcastReceiver的两种注册方式(静态和动态)详解
今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来 ...
- Android中Fragment与Activity之间的交互(两种实现方式)
(未给Fragment的布局设置BackGound) 之前关于Android中Fragment的概念以及创建方式,我专门写了一篇博文<Android中Fragment的两种创建方式>,就如 ...
- JavaScript 函数的两种声明方式
1.函数声明的方式 JavaScript声明函数有两种选择:函数声明法,表达式定义法. 函数声明法 function sum (num1 ,num2){ return num1+num2 } 表达式定 ...
随机推荐
- codeforces164A
Variable, or There and Back Again CodeForces - 164A Life is not easy for the perfectly common variab ...
- Ideal打war包和tomcat展示War包
今天主要是介绍如何把java代码把成war包以及如何在tomcat中放置展示.比较简单.大家可以看看 刚开始ideal 不知道打包,网上搜索了一个教程,看了半天没看会. 主要还是说的不太明白 . 不过 ...
- jQuery 设置select,radio的值,无法自动触发绑定的change事件
一.问题 今天在对select和radio做change事件绑定后,手动设置其value值,但是不能触发change事件 二.解决 使用trigger方法手动触发
- js学习笔记(1)
前言 因为后期的软件工程组队作业需要js,在纪华裕大佬的带领下(我觉得他好像更喜欢纪华裕这个名字),我开始了js的学习.其实这篇博客应该在两天前的晚上就发出来了,因为忙着写个人编程,拖到了现在,组 ...
- LC 990. Satisfiability of Equality Equations
Given an array equations of strings that represent relationships between variables, each string equa ...
- DisplayAttribute应用——根据PropertyName自动获取对应的UI显示名
model定义,使用DisplayAttribute public class AddressSetInfo { /// <summary> /// invoiceAddress.Id / ...
- 安装mycat
1.下载mycat 为了方便,我已经下载下来.我选择的版本是1.6版本 2.解压,安装在/home/xm6f/dev目录下 cd /home/xm6f/devtar -zxvf Mycat-serve ...
- mysql 对应数据库服务器配置 所能承受的tps和qps
总结: 吞吐量实际涵盖了TPS 和 QPS TPS 是指产生事物的请求,比如对数据库 增.删.改 QTP 是对数据库查询动作,无逻辑非事物,比如 查询 假如脚本里面都是get请求,那么出来的吞吐量就是 ...
- sql的游标用法举例(Cursor)
sql的游标用法举例 ), ) Declare authors_cursor Cursor For Select Name,TrueName From Account Open authors_cur ...
- 【Leetcode_easy】594. Longest Harmonious Subsequence
problem 594. Longest Harmonious Subsequence 最长和谐子序列 题意: 可以对数组进行排序,那么实际上只要找出来相差为1的两个数的总共出现个数就是一个和谐子序列 ...