来看这样一道问题:http://acm.dlut.edu.cn/problem.php?id=1210

题目大意:对于一个1-n的排列,a1,a2,a3,a4...an我们把满足i < j,ai > aj这样的数对(ai,aj)成为一个逆序对,另有一个数组b【i】记录aj = i这样的逆序对的个数,例如排列:

3 1 5 2 4,对应的逆序数组b[1] = 1,b2[2] = 2,b[3] = 0,b[4] = 1,b[5] = 0;换句话说,b【i】代表了i之前有多少比它大的数;

问:给出一个b数组,构造出一个符合情况的排列a,1 <=n<=100000;

容易看出,最后的排列一定是唯一的,先来想一想最暴力的解法,其实如果不是因为n的大小是十万级别的话,很容易想到暴力插入的方式,那就是先让大的占位置;

(1)b【5】= 0:先把5放在排列开头:5;

(2)b【4】= 1:4前面比4大的有一个:5,4;

(3)b【3】= 0,3前面比3大的有0,显然:3,5,4;

(4)b【2】= 2:比2大的有2个:3,5,2,4;

(5)b【1】= 1:比1大的有3,1,5,2,4;这就是最终的结果;

按照这种方式插入很定可以构造出最终结果,显然插入一个数时要移动数据元素,最终的复杂度是o(n^2)的,肯定会超时;

同时我们来看POJ2828:

Description

n个人排队,输入第一行为n的值,接下来输入n行,每行一个x值,一个val值,x代表第i个人插入的位置前面有多少个人,i从1到n;

Sample Input

4
0 77
1 51
1 33
2 69
4
0 20523
1 19243
1 3890
0 31492

Sample Output

 77 33 69 51

31492 20523 3890 19243

这两个题几乎就是一个题嘛,对于这种插队问题,需要离线来做;
因为先插进来的人会影响到后面的人,但是对于最后一个插队的人来说如果他前面有x个人,最终他肯定就在x + 1的位置上。所以需要离线处理并且逆序进行插入;
我们用一个sum数组表示位置i有没有空位即能不能插进来一个人,sum【i】 = 1代表这个位置上能插进来一个人,sum = 0表示不能插入;
以poj2828的第一组样例为例:
注意sum下标从0开始
sum:1 1 1 1 1表示5个位置上都可以插入,如果我们把(2,69)插入:
sum:1 1 0 1 1那么肯定第三个位置上的数字是69;再插入(1,33),
注意到33插在69的前面,实际上当33插入的时候,69还没插入,所以69和33根本互不影响,所以33肯定也是插在第1个位置
sum:1 0 0 1 1,我们再插入(1,51),这里就怪了,sum【1】貌似已经被33占据了,那么怎么处理51呢?实际上我们知道,在插入51时,33和69都没进来呢,所以可以暂时认为sum【1】和sum【2】都不存在,
显然51应该插入在第1个1上面,即sum【3】:
sum:1 (0 0) 0 1;注意这里括号括起来的是咱们假想的,真正插入51的时候,33和69还没插进呢,所以51确实插入了第“1”个位置,因为在33,69没插进来时,前面只有一个第0个位置有空。
所以这里插入的原则就很清楚了,对于一个(x,value),应该插入到第x个1的位置,怎么统计位置i前面有几个1呢??那不正是统计sum的前缀和嘛?怎么统计区间和?线段树嘛! 插入(x,value)时,线段树把一个区间分为一个左子区间和一个右左区间,如果左区间还有x个空间可以插入,就应该到左子区间去更新;否则应该到右子区间去查询,但是此时,查询的位置应该减去
sum【rson】用以表示相对的位置,因为sum里面保存的是区间之内有几个位置可以插入,所以进入右子区间时,应该减掉在左子区间里的那些“1”的个数,所以这里用的是相对位置;
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define INF 100000000
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
const int maxn = 2e5 + ;
typedef long long ll; int pos[maxn], val[maxn];
int sum[maxn << ];
int ans[maxn];
int n; void pushup(int rt)
{
sum[rt] = sum[lc] + sum[rc];
}
void build(int rt, int l, int r)
{
if(l == r) {
sum[rt] = ;
return;
}
int m = (l + r) >> ;
build(lc, l, m);
build(rc, m + , r);
pushup(rt);
}
void update(int p, int add, int l , int r , int rt ) {
if(l == r) {
sum[rt] = ;
ans[l] = add;
return ;
}
int m = (l + r) >> ;
if(p <= sum[rt << ])update(p, add, l,m,lc);
else update(p - sum[rt << ], add, m+,r,rc);
pushup(rt);
}
int main()
{
 for(;~scanf("%d", &n);)
{
for(int i = ; i <= n; ++i)
scanf("%d%d", pos + i, val + i);
build(, , n);
for(int i = n; i >= ; --i)
{
int p = pos[i]+;
int v = val[i];
update(p, v, , n, );
}
for(int i = ; i <= n; ++i)
printf("%d%c", ans[i], (i ^ n ? ' ' : '\n'));
memset(ans, , sizeof(ans));
}
}

POJ2828---线段树与逆序数&&DUTOJ1210---逆序对构造排列的更多相关文章

  1. POJ.2299 Ultra-QuickSort (线段树 单点更新 区间求和 逆序对 离散化)

    POJ.2299 Ultra-QuickSort (线段树 单点更新 区间求和 逆序对 离散化) 题意分析 前置技能 线段树求逆序对 离散化 线段树求逆序对已经说过了,具体方法请看这里 离散化 有些数 ...

  2. HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对)

    HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对) 题意分析 给出n个数的序列,a1,a2,a3--an,ai∈[0,n-1],求环序列中逆序对 ...

  3. hdu 1394 Minimum Inversion Number(线段树之 单点更新求逆序数)

    Minimum Inversion Number                                                                           T ...

  4. csu 1555(线段树经典插队模型-根据逆序数还原序列)

    1555: Inversion Sequence Time Limit: 2 Sec  Memory Limit: 256 MBSubmit: 469  Solved: 167[Submit][Sta ...

  5. codeforces_459D_(线段树,离散化,求逆序数)

    链接:http://codeforces.com/problemset/problem/459/D D. Pashmak and Parmida's problem time limit per te ...

  6. POJ2828线段树单点更新——逆序更新

    Description 输入n个有序对< pi, vi >,pi表示在第pi个位置后面插入一个值为vi的人,并且pi是不降的.输出最终得到的v的序列 Input 多组用例,每组用例第一行为 ...

  7. 51nod 1019 逆序数(逆序数+离散化)

    在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数.   如2 4 3 1中,2 1,4 3,4 1,3 1是 ...

  8. 归并求逆序数(逆序对数) && 线段树求逆序数

    Brainman Time Limit: 1000 MS Memory Limit: 30000 KB 64-bit integer IO format: %I64d , %I64u   Java c ...

  9. poj 2828 Buy Tickets【线段树单点更新】【逆序输入】

    Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 16273   Accepted: 8098 Desc ...

随机推荐

  1. storm 事务和DRPC结合

    示例代码: package com.lky.topology; import backtype.storm.Config; import backtype.storm.LocalCluster; im ...

  2. android启动优化

    ############################################## # power on till android lock screen comes up # # get ...

  3. java byte 16进制转换

    整型转16进制: int devIdInt = Integer.parseInt(devId);String devIdString = Integer.toHexString(devIdInt); ...

  4. Linux驱动开发常用头文件

    头文件目录中总共有32个.h头文件.其中主目录下有13个,asm子目录中有4个,linux子目录中有10个,sys子目录中有5个.这些头文件各自的功能如下: 1.主目录 <a.out.h> ...

  5. HID 报告描述符精细说明.

    1,报告描述符概述    1.1) 报表描述符        报表描述符和USB的其他描述符是不一样的,它不是一个简单的表格,报表描述符是USB所有描述符中最复杂的.报表描述符非常复杂而有弹性,因为它 ...

  6. 打开SQL Server 配置管理器时出现了问题

    解决方法: 1.找到sqlmgmproviderxpsp2up.mof的文件位置 2.打开window+R打开命令提示符(输入cmd):输入sqlmgmproviderxpsp2up.mof的文件位置 ...

  7. C# XML流操作简单实例

    这里我们先介绍操作XML文件的两个对象:XmlTextReader和XmlTextWriter打开和读取Xml文件使用到的对象就是XmlTextReader对象.下面的例子打开了与程序在同一路径下的一 ...

  8. HC-05蓝牙模块基本使用

    1.进入AT模式 EN输入高电平+按住按键不放,然后上电,进入AT模式,不过AT指令只能输入一次,下次再输入AT需要重新进入 2.串口波特率设为38400,进行AT模式下的指令操作 3.基本AT指令 ...

  9. 【干货】国外程序员整理的 C++ 资源大全【转】

    来自 https://github.com/fffaraz/awesome-cpp A curated list of awesome C/C++ frameworks, libraries, res ...

  10. js判断一个变量是否为数组的解决方案

    前端开发中,在做项目的时候,我们经常需要对一个变量进行数组类型的判断,当然即使你暂时没遇到,但是这个问题也是大家去面试时的高频问题,有必要拿出来说一说. 大家都知道js中可以使用typeof来判断变量 ...