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 } 表达式定 ...
随机推荐
- Hadoop(1)---运行Hadoop自带的wordcount出错问题。
在hadoop2.9.0版本中,对namenode.yarn做了ha,随后在某一台namenode节点上运行自带的wordcount程序出现偶发性的错误(有时成功,有时失败),错误信息如下: // : ...
- MySQL的sql解析
首先看一下示例语句 SELECT DISTINCT < select_list > FROM < left_table > < join_type > JOIN ...
- 在OpenFOAM中做用户自定义库——编译library【转载】
转载自:http://openfoam.blog.sohu.com/22041538.html OpenFOAM自己提供的标准类都是以库的形式提供的,并且利用头文件给出了库的应用接口.这样一来,用户的 ...
- Apache Flink - Window
Window: 在Streaming中,数据是无限且连续的,我们不可能等所有数据都到才进行处理,我们可以来一个就处理一下,但是有时我们需要做一些聚合类的处理,例如:在过去的1分钟内有多少用户点击了我们 ...
- xposed代码示例
package com.example.xposedhook; import android.util.Log; import de.robv.android.xposed.IXposedHookLo ...
- 2018-2019-2 网络对抗技术 20165222 Exp 8 Web基础
1.实践内容 (1).Web前端HTML 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HTML. 使用service apache2 start启 ...
- Ionic4.x 中的button
官方文档:https://ionicframework.com/docs/api/button 1.ion-button 组件可以定义一个按钮 <ion-button>Default< ...
- ISO/IEC 9899:2011 条款6.2.3——标识符的名字空间
6.2.3 标识符的名字空间 1.如果一个特定标识符的多个声明在一个翻译单元的任意一点可见,那么语法上下文会区分对不同实体的引用.从而,对于标识符各种不同的类别具有独立的名字空间: ——标签名(通过标 ...
- 不同三维引擎渲染IFC数据效果对比
- Qt编写自定义控件53-自定义宽高下拉框
一.前言 默认的qcombobox控件,如果元素item中的内容过长超过控件本身的宽度的话,会自动切掉变成省略号显示,有些应用场景不希望是省略号显示,希望有多长就显示多长,还有一种应用场景是需要设置下 ...