来看这样一道问题: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. switchover和failover

    Dataguard中primary和standby间的角色切换包括两种:1. switchoverprimary和standby互换角色,一般都是人为的有计划的,主要用于主机或数据库的升级,不会有数据 ...

  2. 数据库中的schema概念

    原文地址:http://blog.sina.com.cn/s/blog_7952e89001010jlj.html 数据库的初学者往往会对关系型数据库模式(schema).数据库(database). ...

  3. Linux网络配置相关

    路由相关 #添加到主机的路由 route add -host 192.168.1.2 dev eth0 route add -host 192.168.1.2 gw 192.168.1.1 注1:添加 ...

  4. linux ubuntu 浏览器 字 字体 虚 解决办法

    在刚装好的ubuntu上什么都好,只是浏览器字体很虚,就连百度都虚 解决办法很简单 安装雅黑字体 修改font conf sudo vi /etc/fonts/conf.avail/69-langua ...

  5. [转载]SQL Server查找包含某关键字的存储过程3种方法

    存储过程都写在一个指定的表中了,我们只要使用like查询就可以实现查询当前这台SQL Server中所有存储过程中包括了指定关键字的存储过程并显示出来,下面一起来看看我总结了几条命令. 例子1 代码如 ...

  6. IE6下的怪异解析知识点补充

    转载请注明出处:HTMl5自由者      

  7. 浅谈C++中的那些内存泄露

    尽管学过C语言.可是C++里面的一些基础还是不太懂,还须要再掌握. 老范也開始要讲C++设计模式了,必须快点看了.不然就要白花窝滴钱了. 对于内存泄露,我的个人理解就是程序在执行过程中,自己开辟了空间 ...

  8. C程序设计的抽象思维-算法分析-大多数元素

    [问题] 请编写下面函数 int MajorityElement(int array[],int n); 该函数返回数组array中的多数元素.多数元素是指在占绝对多数(至少51%)的一个值.假设多数 ...

  9. Android 自定义控件玩转字体变色 打造炫酷ViewPager指示器

    1.概述 本篇博客的产生呢,是因为,群里的哥们暖暖给我发了个效果图,然后问我该如何实现顶部ViewPager指示器的字体变色,该效果图是这样的: 大概是今天头条的app,神奇的地方就在于,切换View ...

  10. (三)《Java编程思想》——构造函数初始化

    1.初始化顺序是由变量在类内的定义顺序决定的,并且先初始化变量,然后才调用构造函数. package chapter4; //: OrderOfInitialization.java /** * 初始 ...