线段树求逆序数方法 HDU1394&&POJ2299
为什么线段树能够求逆序数?
给一个简单的序列 9 5 3 他的逆序数是3
首先要求一个逆序数有两种方式:能够从头開始往后找比当前元素小的值,也能够从后往前找比当前元素大的值,有几个逆序数就是几。
线段树就是应用从后往前找较大值得个数。(一边更新一边查)
当前个数是 n = 10
元素 9 5 3
9先增加线段树,T【9】+=1;查从T【9】到T【10】比9大的值,没有sum = 0。
5 增加线段树。T【5】 += 1。查从T【5】到T【10】比5大的值。有一个9。sum +=1;
3 增加线段树,T【3】 += 1,查从T【3】到T【10】比3大的值,有两个9和5,sum +=2;
终于sum = 3;
若元素值不能确定。那么首先讲数列离散化,在从前往后枚举。找后面比前面小的统计个数……
离散化:比如2 5 8 3 10 等价于 1 3 4 2 5,能够通过排序加小小处理解决。
比如:
元素 9 5 3 4 1 离散化后得到索引
索引 1 2 3 4 5
将元素升序排序
元素 1 3 4 5 9
索引 5 3 4 2 1
枚举到第i个数,我们须要求出从1到i-1中有多少个比a[i]大的数,更新答案。
详细怎么做呢?
每次枚举完一个数之后。将这个数插入到线段树里(注意:插到与其相应的位置)。
举个样例:3 2 4 1。
则线段树的变化应该为:t[3]+=1;t[2]+=1;t[4]+=1;t[1]+=1;
设x=a[i],这样,在插入一个数X时,首先求一下t[x+1]~t[n]的和,这个和就是1~i-1中有多少个比a[i]大的数
由于求一个数列的逆序数能够从两个
在你插a[i]时,前面已经有多少个比a[i]大的数
POJ 2299
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define MAX INT_MAX
#define MIN INT_MIN
#define LL __int64
#define init(a) memset(a,0,sizeof(a))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 500010;
using namespace std; int T[maxn<<2], index[maxn], num[maxn];
int n; int cmp(const int i, const int j)
{
return num[i] < num[j];
} void update( int l, int r,int rt, int num, int add)
{
T[rt] += add;
if(r==l) return ;
int m = (l+r) >> 1;
if(num <= m) update(lson, num, add);
else update(rson, num, add);
} int query( int l, int r, int rt,int L, int R)
{
if(L<=l && r<=R) return T[rt];
int m = (l+r) >> 1;
int ans = 0;
if(L <= m) ans += query(lson, L, R);
if(R > m) ans += query(rson, L, R);
return ans;
} int main()
{
while(~scanf("%d", &n))
{
if(n==0) break;
init(index);
for(int i=1;i<=n;i++)
{
scanf("%d", &num[i]);
update(1, n,1, i, 1);//离散化
index[i] = i;
}
sort(index+1, index+n+1,cmp); //排序num。更新索引
LL sum = 0;
for(int i=1;i<=n;i++)
{
sum += (query( 1, n,1, 1, index[i])-1);
update(1, n,1, index[i], -1);
}
printf("%I64d\n", sum);
}
return 0;
}
HDU 1394
题意:一组数 每次把最前面的元素放在最后生成新的序列。再统计逆序数,求全部逆序中最小值
有一个公式:先求出原数列的逆序数。sum
那么每次新逆序数 = sum-a[i] + (n-a[i] - 1)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define MAX INT_MAX
#define MIN INT_MIN
#define LL __int64
#define init(a) memset(a,0,sizeof(a))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 500010;
using namespace std; int T[maxn<<2], index[maxn], num[maxn];
int n; int cmp(const int i, const int j)
{
return num[i] < num[j];
} void update( int l, int r,int rt, int num, int add)
{
T[rt] += add;
if(r==l) return ;
int m = (l+r) >> 1;
if(num <= m) update(lson, num, add);
else update(rson, num, add);
} int query( int l, int r, int rt,int L, int R)
{
if(L<=l && r<=R) return T[rt];
int m = (l+r) >> 1;
int ans = 0;
if(L <= m) ans += query(lson, L, R);
if(R > m) ans += query(rson, L, R);
return ans;
} int main()
{
while(~scanf("%d", &n))
{
if(n==0) break;
init(index);
for(int i=1;i<=n;i++)
{
scanf("%d", &num[i]);
update(1, n,1, i, 1);
index[i] = i;
}
sort(index+1, index+n+1,cmp); LL sum = 0;
for(int i=1;i<=n;i++)
{
sum += (query( 1, n,1, 1, index[i])-1);
update(1, n,1, index[i], -1); }
// printf("%I64d\n", sum);
LL ans = sum;
for(int i = 1;i<=n;i++)
{
sum = sum - num[i] + (n-num[i]- 1);
ans = min(sum,ans);
}
cout<<ans<<endl;
}
return 0;
}
线段树求逆序数方法 HDU1394&&POJ2299的更多相关文章
- HDU_1394_Minimum Inversion Number_线段树求逆序数
Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java ...
- HDU-1394 Minimum Inversion Number(线段树求逆序数)
Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Ot ...
- <Sicily>Inversion Number(线段树求逆序数)
一.题目描述 There is a permutation P with n integers from 1 to n. You have to calculate its inversion num ...
- hdu1394--Minimum Inversion Number(线段树求逆序数,纯为练习)
Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Ot ...
- hdu 1394 (线段树求逆序数)
<题目链接> 题意描述: 给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找 ...
- 【线段树求逆序数】【HDU1394】Minimum Inversion Number
题目大意: 随机给你全排列中的一个,但不断的把第一个数丢到最后去,重复N次,形成了N个排列,问你这N个排列中逆序数最小为多少 做法: 逆序数裸的是N^2 利用线段树可以降到NlogN 具体方法是插入一 ...
- hdu1394 Minimum Inversion Number (线段树求逆序数&&思维)
题目传送门 Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K ...
- HDU - 1394 Minimum Inversion Number (线段树求逆序数)
Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs ( ...
- HDU - 1394 Minimum Inversion Number(线段树求逆序数---点修改)
题意:给定一个序列,求分别将前m个数移到序列最后所得到的序列中,最小的逆序数. 分析:m范围为1~n,可得n个序列,求n个序列中最小的逆序数. 1.将序列从头到尾扫一遍,用query求每个数字之前有多 ...
随机推荐
- c# 字符串编码问题
一. ASCII码 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte). ...
- hdu 2104
#include <iostream> using namespace std; int gcd(int a,int b) { return (b?gcd(b,a%b):a); } int ...
- requestAnimationFrame 兼容处理
(function() { ; var vendors = ['ms', 'moz', 'webkit', 'o']; ; x < vendors.length && !wind ...
- jquery ajax 跨域处理
今天使用JQuery Ajax 在本地电脑获取远程服务器数据的时候,发现使用$.ajax,$.getJSON,$.get这些都没有反应,后来再统一个网站下测试了一下,代码写得没有问题.后来想了想好想, ...
- PL/SQL客户端安装配置说明
一.电脑安装了多个Oracle客户端时,需要设定pl/sql 中的home 二.配置环境变量: (打开环境变量配置界面操作:我的电脑---属性---高级---环境变量,在系统变量部分新建或编辑即可.w ...
- 数据库WMI 0x80041010 如何解决?
在你打开 SQL Server Configuration Manager遇到以下错误的时候,请参考下面提出的解决办法 solution: 打开cmd 命令窗口执行mofcomp.exe " ...
- sqlserver2012 评估期已过问题处理
由于之前安装sqlserver2012忘记输入序列号,现在出现评估期已过的问题,网上忙活半天,才解决,发现网上叙述都很凌乱,而且只有大意,新手很难操作,所以把我操作的过程分享给大家 1 开始菜单找到s ...
- 3.4 C与汇编程序的相互调用
为了提高代码执行效率,内核源代码中有些地方直接使用了汇编语言编制.这就会涉及在两种语言编制的程序之间相互调用的问题. 函数调用包括从一块代码到另一块代码之间的双向数据传递和执行控制转移.数据传递通过函 ...
- thinkphp3.23整合phpexcel
HINKPHP3.2.3整合PHPexcel实现数据的导入导出.可以上传excel文件后批量导入到数据库,兼容.xls和.xlsx格式:数据库里的数据可以按照搜索条件和分页导出为excel文件.最近接 ...
- linux2.6.32 内核源码树解析与整理
一 系统最核心组件目录: 1 arch目录该目录中的每个子目录中都与某种体系结构相对应,用于存放体系结构相关代码,向平台无关的系统核心模块提供所需的功能接口.每个体系结构对应的子目录下通常至少包含以下 ...