POJ-2299 Ultra-QuickSort---树状数组求逆序对+离散化
题目链接:
https://vjudge.net/problem/POJ-2299
题目大意:
本题要求对于给定的无序数组,求出经过最少多少次相邻元素的交换之后,可以使数组从小到大有序。
两个数(a, b)的排列,若满足a > b,则称之为一个逆序对。
n < 500,000 0 ≤ a[i] ≤ 999,999,999
解题思路:
由于数据范围大,可以考虑离散化。
为什么要离散化?
离散化的目的就在于将这么多的数字转化成1-500000以内,然后开一个tree树状数组,下标就对应着数值
如何离散化?
从小到大离散化成1-n,比如数组9 1 0 5 4 离散化成5 2 1 4 3,然后就可以用树状数组做了。
开了树状数组,接下来怎么做?
从左往右依次往树状数组中加入元素,每次加入的时候,在对应下标的位置的数字加一,加入之后数一下在这个下标后面有多少个1,就是加入该数字的逆序对的数目
下面进行模拟 5 2 1 4 3,模拟之后你就懂了
最初的树状数组:
1、首先加入5,此时树状数组的第5个元素+1(红块表示加1),此时5的后面没有元素,所以加入5的逆序对为0,ans = 0
2、加入2,此时第2个元素加1,2的后面有一个红块(表示加一),所以加入2的逆序对为1, ans = 1
3、加入1,此时第1个元素加1,1的后面有两个红块(表示加一),所以加入1的逆序对为2, ans = 3
4、加入4,此时第4个元素加1,4的后面有一个红块(表示加一),所以加入4的逆序对为1, ans =4
5、加入3,此时第3个元素加1,3的后面有两个红块(表示加一),所以加入3的逆序对为2, ans = 6
利用树状数组可以在o(log(n))的时间复杂度求出当前数字的前缀和,进而可以求出在当前数字后面数字的个数(i-sum(x))(i表示已经加入的总数字的数目,sum(x)表示小于等于x的数字的数目,它们之差就是大于x的数字的数目)
这样就把逆序对问题和树状数组联系起来了。
还有需要注意的地方:
如果,数据之中有数字相等的情况,离散化应该怎么处理呢?
举个例子 2 2 2 2,如果离散化成1 2 3 4,那么每次加入的时候在树状数组中找比它大的元素个数,求出的逆序对为0,正确,这种处理不会产生冲突
如果离散化成4 3 2 1,求出的解时6,答案错误,所以在离散化的时候,权值小的离散之后的值小,权值相同的,下标在前面的离散后的值小。
小技巧:
用结构体存权值和id,排序之后根据id创建新的离散化后的数组
上代码:
#include<iostream>
#include<algorithm>
#include<set>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = + ;
typedef long long ll;
struct node
{
int x, id;
bool operator < (const node& a)const
{
return x < a.x || x == a.x && id < a.id;
//从小到大排序,如果x相等,那么编号小的排在前面
//这是因为这样的话之后离散化的时候,编号小的离散化的数字也是小的
//之后求逆序对时需要按照原来ID顺序一个一个放离散化的数字
//相同的x最开始放入的值是小的,后面放入的值是大的,这样不会额外增加逆序对
//比如一个数组2 2 2 2 按照上述方法离散化成1 2 3 4,逆序对为0。
//如果离散化成4 3 2 1,则逆序对就会求错了
}
}a[maxn];
int b[maxn];
int tree[maxn];
int n;
int lowbit(int x)
{
return x & (-x);
}
void add(int x, int d)
{
while(x <= n)
{
tree[x] += d;
x += lowbit(x);
}
}
ll sum(int x)
{
ll ret = ;
while(x > )//此处等于0会导致无限循环
{
ret += tree[x];
x -= lowbit(x);
}
return ret;
}
int main()
{
while(cin >> n && n)
{
memset(tree, , sizeof(tree));
memset(a, , sizeof(a));
memset(b, , sizeof(b));
for(int i = ; i <= n; i++)
{
scanf("%d", &a[i].x);
a[i].id = i;
}
sort(a + , a + n + );
for(int i = ; i <= n; i++)
{
b[a[i].id] = i;//离散化操作,根据原来的id,进行大小的编号,从小到大编号1-n
}
ll ans = ;
for(int i = ; i <= n; i++)
{
add(b[i], );//将b[i]加入树状数组中
ans += i - sum(b[i]);//i-sum(b[i])表示目前加入了i个数,其中有sum(b[i])个数字比b[i]小,相减的结果就是目前比b[i]大的数字数目
}
cout<<ans<<endl;
}
return ;
}
离散化的另一种方式
之前是从小到大离散化,现在从大到小离散化,9 1 0 5 4 离散化成1 4 5 2 3,那进行树状数组求值的时候,每加入一个数,求前面比它小的数字即可,正好是树状数组的sum函数的作用
比如上述例子
1 4 5 2 3
加入1时,没有比1小的,ans=0
加入4时,有1个比4小,ans = 1;
加入5是,有2个比5小,ans = 3;
。。。。。。
同理,上述的有重复元素的时候2 2 2 2离散化成1 2 3 4的时候是错误的,因为这里是找比该数小的数字,所以1 2 3 4求出逆序对为6,是错误的
离散化成4 3 2 1的话,就是正确的。
上代码:(找不同,好好看看就懂了)
#include<iostream>
#include<algorithm>
#include<set>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = + ;
typedef long long ll;
struct node
{
int x, id;
bool operator < (const node& a)const
{
return x > a.x || x == a.x && id > a.id;//这里变啦!!!
}
}a[maxn];
int b[maxn];
int tree[maxn];
int n;
int lowbit(int x)
{
return x & (-x);
}
void add(int x, int d)
{
while(x <= n)
{
tree[x] += d;
x += lowbit(x);
}
}
ll sum(int x)
{
ll ret = ;
while(x > )//此处等于0会导致无限循环
{
ret += tree[x];
x -= lowbit(x);
}
return ret;
}
int main()
{
while(cin >> n)
{
memset(tree, , sizeof(tree));
memset(a, , sizeof(a));
memset(b, , sizeof(b));
for(int i = ; i <= n; i++)
{
scanf("%d", &a[i].x);
a[i].id = i;
}
sort(a + , a + n + );
for(int i = ; i <= n; i++)
{
b[a[i].id] = i;//离散化操作,根据原来的id,进行大小的编号,从大到小编号1-n
}
ll ans = ;
for(int i = ; i <= n; i++)//还有下面的两行
{
ans += sum(b[i]);//下标比b[i]小,但是实际的数字比b[i]大(因为离散化的时候就是数字大的编号小)
add(b[i], );//要先调用sum,再调用add,因为先调用add的话,求sum的时候把自己也算进去了
}
cout<<ans<<endl;
}
}
POJ-2299 Ultra-QuickSort---树状数组求逆序对+离散化的更多相关文章
- poj 2299 Ultra-QuickSort(树状数组求逆序数+离散化)
题目链接:http://poj.org/problem?id=2299 Description In this problem, you have to analyze a particular so ...
- poj 2299 Ultra-QuickSort(树状数组求逆序数)
链接:http://poj.org/problem?id=2299 题意:给出n个数,求将这n个数从小到大排序,求使用快排的需要交换的次数. 分析:由快排的性质很容易发现,只需要求每个数的逆序数累加起 ...
- hdu5792 World is Exploding(多校第五场)树状数组求逆序对 离散化
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5792 题目描述:给你n个值,每个值用A[i]表示,然后问你能否找到多少组(a,b,c,d)四个编号,四 ...
- POJ2299Ultra-QuickSort(归并排序 + 树状数组求逆序对)
树状数组求逆序对 转载http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html 转载: 树状数组,具体的说是 离散化+树 ...
- [NOIP2013提高&洛谷P1966]火柴排队 题解(树状数组求逆序对)
[NOIP2013提高&洛谷P1966]火柴排队 Description 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相 ...
- [NOI导刊2010提高&洛谷P1774]最接近神的人 题解(树状数组求逆序对)
[NOI导刊2010提高&洛谷P1774]最接近神的人 Description 破解了符文之语,小FF开启了通往地下的道路.当他走到最底层时,发现正前方有一扇巨石门,门上雕刻着一幅古代人进行某 ...
- 【bzoj2789】[Poi2012]Letters 树状数组求逆序对
题目描述 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. 输入 第一行一个正整数n ...
- “浪潮杯”第九届山东省ACM大学生程序设计竞赛(重现赛)E.sequence(树状数组求逆序对(划掉))
传送门 E.sequence •题意 定义序列 p 中的 "good",只要 i 之前存在 pj < pi,那么,pi就是 "good": 求删除一个数, ...
- 2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对)
2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对) https://www.luogu.com.cn/problem/P5041 题意: 给一个字符串 \(S\) ,每 ...
- NOIP 2013 洛谷P1966 火柴排队 (树状数组求逆序对)
对于a[],b[]两个数组,我们应选取其中一个为基准,再运用树状数组求逆序对的方法就行了. 大佬博客:https://www.cnblogs.com/luckyblock/p/11482130.htm ...
随机推荐
- PHP冒泡排序、选择排序、插入排序
$arr = [1, 8, 7, 5, 4, 2, 11, 9, 20]; 冒泡排序: for ($i = 0; $i < count($arr); $i ++) { for ($j = 0; ...
- spring8——AOP之Bean的自动代理生成器
对于上篇博客http://www.cnblogs.com/cdf-opensource-007/p/6464237.html结尾处提到的两个问题,可以使用spring提供的自动代理生成器解决.自动代理 ...
- django的models模块查询方法
假定models中有一个类BookInfo 模块查询不同于sql语句,模块查询的结果会返回符合条件的整个一行的对象,或者多个对象组成的查询集. 查询集类似列表,有相似的方法. 1 model查询语句: ...
- linux下安装 配置 redis数据库
通过终端命令安装(推荐): 1 确保更新源服务器能正常使用 如果没有更换更新源服务器,那么可能一直都下不了软件.欢迎参考我之前的博文来更换成国内的镜像服务器http://www.cnblogs.com ...
- Hibernate(二):MySQL server version for the right syntax to use near 'type=InnoDB' at line x
目前使用的hibernate5.2.9版本,配置的mysql方言为: <property name="hibernate.dialect">org.hibernate. ...
- jacascript DOM节点——节点获取与选择器API
前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! DOM 操作必须等待 HTML 加载完毕之后,才可以获取节点:有两种方法: 把 script 标签放到代码 ...
- YII2框架下使用PHPExcel导出柱状图
导出结果: 首先,到官网下载PHPExcel插件包,下载后文件夹如下: 将Classes文件夹放入到项目公共方法内. 新建控制器(访问导出的方法):EntryandexitController < ...
- 【转】如何使用slave_exec_mode优雅的跳过1032 1062的复制错误
今天线上的主从复制发生1062的错误,使用sql_slave_skip_counter跳过之后,由于后面的事务需要对刚刚的数据进行update,后续造成了新的1032的错误. 后来,无意中发现还有更好 ...
- 在一个没有设置宽高的容器中,为什么设置position:absolute后就可以全屏显示了?
此场景适用于移动端百分比布局,背景全屏显示. 在一个没有设置宽高的容器中设置背景,想要背景全屏显示,设置bcakground-size:100%;后还需设置position:absolut; 原因: ...
- java中的接口概念
接口的特点: 接口中只有抽象方法和全局常量 public interface className{} 范例:简单的代码模型 interface A{ public static final Strin ...