此题有两种建树方式!

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-线段树的两种建树方式的更多相关文章

  1. 线段树:HDU2795-Billboard(建树方式比较新奇)

    Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...

  2. POJ 3225 线段树区间更新(两种更新方式)

    http://blog.csdn.net/niuox/article/details/9664487 这道题明显是线段树,根据题意可以知道: (用0和1表示是否包含区间,-1表示该区间内既有包含又有不 ...

  3. CodeForces Round #179 (295A) - Greg and Array 一个线段树做两次用

    线段树的区间更新与区间求和...一颗这样的线段树用两次... 先扫描1~k...用线段树统计出每个操作执行的次数... 那么每个操作就变成了 op. l  , op.r , op.c= times* ...

  4. poj 3667 Hotel (线段树的合并操作)

    Hotel The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a ...

  5. 巨蟒python全栈开发数据库前端6:事件onclick的两种绑定方式&&onblur和onfocus事件&&window.onload解释&&小米商城讲解

    1.回顾上节内容(JavaScript) 一.JavaScript概述 1.ECMAScript和JavaScript的关系 2.ECMAScript的历史 3.JavaScript是一门前后端都可以 ...

  6. Web APi之认证(Authentication)两种实现方式【二】(十三)

    前言 上一节我们详细讲解了认证及其基本信息,这一节我们通过两种不同方式来实现认证,并且分析如何合理的利用这两种方式,文中涉及到的基础知识,请参看上一篇文中,就不再叙述废话. 序言 对于所谓的认证说到底 ...

  7. Android中BroadcastReceiver的两种注册方式(静态和动态)详解

    今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来 ...

  8. Android中Fragment与Activity之间的交互(两种实现方式)

    (未给Fragment的布局设置BackGound) 之前关于Android中Fragment的概念以及创建方式,我专门写了一篇博文<Android中Fragment的两种创建方式>,就如 ...

  9. JavaScript 函数的两种声明方式

    1.函数声明的方式 JavaScript声明函数有两种选择:函数声明法,表达式定义法. 函数声明法 function sum (num1 ,num2){ return num1+num2 } 表达式定 ...

随机推荐

  1. 小程序开发--WePy框架

    现如今mvvm框架如此火热,其核心思想即js逻辑层不直接操作DOM,只改变组件状态:而视图层则通过模板template进行渲染. 1.WePy项目的目录结构 ├── dist 小程序运行代码目录 ├─ ...

  2. PHP 之源代码加密与解密,加密后可直接运行

    方式一: <?php /** * Created by PhpStorm. * User: Yang * Date: 2019/10/16 * Time: 10:25 */ class Enci ...

  3. 深入理解WebRTC

    Web Real-Time Communication(Web实时通信,WebRTC)由一组标准.协议和JavaScript API组成,用于实现浏览器之间(端到端)的音频.视频及数据共享. WebR ...

  4. CSAW Quals CTF 2017-scv

    目录 程序基本信息 程序漏洞 整体思路 exp脚本 内容参考 程序基本信息 64位动态链接程序,开启了栈溢出和数据段不可执行保护 程序漏洞 read函数很明显的栈溢出漏洞 整体思路 由于题目给了lib ...

  5. Microsoft Visual C++ Runtime library not enough space for thread data

    Microsoft Visual C++ Runtime library not enough space for thread data     电脑最近一直在运行的时候,弹出提示框,如下: 解决办 ...

  6. return EXIT_SUCCESS;

    就是 return 0; EXIT_SUCCESS是C语言头文件库中定义的一个符号常量. 头文件stdlib.h中:#include <cstdlib> /* Definition of ...

  7. ios 新建app iphone 、 ipad or universal ?

    很久没有关注这个新建app的  时候 选什么的问题了, 因为我们一般在公司 都是 已经建立好的app 直接 在那上面开发. 所以很久不建立新app 遇到新的app需要你自己去创建的时候 可能就会 有突 ...

  8. keras启用tensorboard

    在callback函数中添加tensorboard,启用tensorboard. # TensorBoard callback tensorboard_cb = K.callbacks.TensorB ...

  9. shell统计ip访问情况并分析访问日志

    有日志 1.log,部分内容如下: 112.111.12.248 – [25/Sep/2013:16:08:31 +0800]formula-x.haotui.com“/seccode.php?upd ...

  10. spark "main" java.lang.ArrayIndexOutOfBoundsException: 10582

    升级 你的 paranamer 到2.8 ,这是由于你的jdk版本1.8导致 <!-- https://mvnrepository.com/artifact/com.thoughtworks.p ...