uva 11525(线段树)
题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2520
题意:有一个排列1~k,求第n个排列,其中n为
,K(1≤K≤50000),S1, S2 ,…, Sk.(0≤Si≤K-i).
分析:这道题目乍看之下没有什么好的思路,k!太大了,但是仔细看一看就会发现n和康托展开式很类似
如果不知道康托展开的话请看:http://www.doc88.com/p-293361248346.html
http://blog.csdn.net/morgan_xww/article/details/6275460
要求第n个全排列,这不就是逆康托展开吗?
没错,仔细对比逆康托展开的推理过程就会发现,其实第n个全排列中的第i个数就是该排列中未出现过的比si大的第一个数。
比如:2 1 0 则比2大的第一个数是3,3未出现过,所以第一个数是3
比1大的第一个数是2,2未出现过,所以第二个数是2
比0大的第一个数是1,1未出现过,所以第三个数是1
所以结果为3 2 1
再比如:1 0 0 则比1大的第一个数是2,2未出现过,所以第一个数是2
比0 大的第一个数是1,1未出现过,所以第二个数是1
比0大的第一个数是1,但是1,2已经出现过了,所以第三个数是3
以此类推
普通的逆康托展开复杂度是O(n^2),这样对于k<=50000来说肯定是会超时的,可以用线段树(二分+树状数组)优化。
由上面的分析可知,
解法1:
线段树的具体做法同样是把 [1, K] 的数置成 1. 此时每条线段的权所代表的意义为在该区间内还有多少个数可以用。查找大于si的第一个未出现过的数,然后把这个数赋为0。 查找的时候如果左线段可用的数大于等于当前我们查找的数, 说明我们要找的数在左线段, 进入左线段, 查找的数不变; 否则说明在右线段, 进入右线段, 查找的数减去左线段可用的数的数目. 递归返回条件为当前节点代表的线段为单位线段(说明我们已经找到了)。
AC代码如下:
#include<stdio.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=+;
int tree[maxn<<],ans;
void PushUp(int rt)
{
tree[rt]=tree[rt<<]+tree[rt<<|];
}
void build(int l,int r,int rt)
{
if(l==r)
{
tree[rt]=;
return ;
}
int m=(l+r)>>;
build(lson);
build(rson);
PushUp(rt);
}
void update(int p,int x,int l,int r,int rt)
{
if(l==r)
{
tree[rt]=x;
ans=l;
return ;
}
int m=(l+r)>>;
if(p<=tree[rt<<])
update(p,x,lson);
else
update(p-tree[rt<<],x,rson);
PushUp(rt);
}
int main()
{
int t,n,i,x;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
build(,n,);
for(i=;i<n;i++)
{
scanf("%d",&x);
update(x+,,,n,);
printf("%d",ans);
if(i!=n-)
printf(" ");
}
printf("\n");
}
return ;
}
解法2:
树状数组的具体做法是初始把 [1, K] 的数置成 1, 然后根据所给 S 数组, 去查找前缀和, 前缀和 sum[N] 代表 [1, N] 内有多少个数可以用. 注意前缀和是单调不减的, 因此我们可以二分, 查找第一个等于我们要找的数/的那个下标, 便是全排列当前位的数, 然后把这个下标里的数置成 0, 同时更新树状数组.
AC代码如下:
#include<stdio.h>
#include<string.h>
const int maxn=+;
int c[maxn],num[maxn];
int n;
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int num)
{
while(x<=n)
{
c[x]+=num;
x+=lowbit(x);
}
}
int sum(int x)
{
int ret=;
while(x>)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
int binary(int x)
{
int low=,high=n;
while(low<=high)
{
int m=(low+high)>>;
int cnt=sum(m);
if(cnt==x)
{
if(num[m])
return m;
else
high=m-;
}
else if(cnt<x)
low=m+;
else
high=m-;
}
return ;
}
int main()
{
int t,i,x;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
memset(c,,sizeof(c));
for(i=;i<=n;i++)
num[i]=;
for(i=;i<=n;i++)
update(i,);
for(i=;i<n;i++)
{
scanf("%d",&x);
int cnt=binary(x+);
update(cnt,-);
num[cnt]=;
printf("%d",cnt);
if(i!=n-)
printf(" ");
}
printf("\n");
}
return ;
}
uva 11525(线段树)的更多相关文章
- UVA 11297 线段树套线段树(二维线段树)
题目大意: 就是在二维的空间内进行单个的修改,或者进行整块矩形区域的最大最小值查询 二维线段树树,要注意的是第一维上不是叶子形成的第二维线段树和叶子形成的第二维线段树要 不同的处理方式,非叶子形成的 ...
- UVa 11992 (线段树 区间修改) Fast Matrix Operations
比较综合的一道题目. 二维的线段树,支持区间的add和set操作,然后询问子矩阵的sum,min,max 写完这道题也是醉醉哒,代码仓库里还有一份代码就是在query的过程中也pushdown向下传递 ...
- UVa 1400 (线段树) "Ray, Pass me the dishes!"
求一个区间的最大连续子序列,基本想法就是分治,这段子序列可能在区间的左半边,也可能在区间的右半边,也有可能是横跨区间中点,这样就是左子区间的最大后缀加上右子区间的最大前缀之和. 线段树维护三个信息:区 ...
- UVA 11992 线段树
input r c m r<=20,1<=m<=20000 m行操作 1 x1 y1 x2 y2 v add v 2 x1 y1 x2 y2 v s ...
- uva 1513(线段树)
题目链接:1513 - Movie collection 题意:有一堆电影,按1-n顺序排,有m次操作,每次询问第ai个电影之前有多少个电影,然后将其抽出放在堆顶. 分析:线段树应用. 因为每次查询后 ...
- uva 12086 线段树or树状数组练习
题目链接 https://vjudge.net/problem/34215/origin 这个题就是线段树裸题,有两种操作,实现单点更新和区间和的查找即可,这里第一次学习使用树状数组完成. 二者相 ...
- UVa 12299 线段树 单点更新 RMQ with Shifts
因为shift操作中的数不多,所以直接用单点更新模拟一下就好了. 太久不写线段树,手好生啊,不是这错一下就是那错一下. PS:输入写的我有点蛋疼,不知道谁有没有更好的写法. #include < ...
- UVA 12299 线段树 ( 单点跟新 , 区间查询)
题目链接:题意:在传统的RMQ的基础上加上一个操作:shift(i1,i2,i3...ik),表示将这些元素,依次向左移动一位(训练指南247页) #include <iostream> ...
- UVA 11992 ——线段树(区间修改)
解题思路: 将矩阵每一行建立一棵线段树,进而变成一维问题求解.注意数组要开 4*N 代码如下: #include <iostream> #include <cstdio> #i ...
随机推荐
- 【RedHat Linux】 链路聚合
[链路聚合] RHEL 7运行模式:teamd 守护进程, teamdctlroundrobin 轮询activebackup 热备份, 其中一个位为backuploadbalance 负载均衡lac ...
- Codeforces 915 C. Permute Digits (dfs)
题目链接:Permute Digits 题意: 给出了两个数字a,b(<=1e18),保证a,b都不带前缀0.用a的字符重组一个数字使这个值最大且小于b.(保证这个值存在) 题解: 这题遇到了不 ...
- c3p0 ComboPooledDataSource无法识别的问题
maven项目下,基本就是导错包了的问题. 下面那个才是连接池的.
- 网易公开课[一万分钟]《office办公达人养成计划》
note: Shift表示移动 Ctrl表示复制 Excel: 快速选中单元格: 选中表格左上角Ctrl+A Ctrl+方向键,跳到行或列的尽头 Ctrl+Shift+方向键,选中一行或一列 Shif ...
- SICP读书笔记 2.2
SICP CONCLUSION 让我们举起杯,祝福那些将他们的思想镶嵌在重重括号之间的Lisp程序员 ! 祝我能够突破层层代码,找到住在里计算机的神灵! 目录 1. 构造过程抽象 2. 构造数据抽象 ...
- Web服务架构
# Web服务架构 ### Web服务模型-- 服务提供者.服务请求者.服务注册中心,服务注册中心是一个可选的角色. 现在的Web服务不仅限于WSDL,还有RESTful. - 服务提供者.即Web服 ...
- 正确配置 debian squeeze apt 源
本想在 Debian Squeeze 上安装一些依赖,没想到刚执行 apt-get update ,就出现这样的错误信息. W: GPG error: http://mirrors.163.com s ...
- thrift0.5入门操作
在探索未知的程序之前,我们往往会使用“Hello World”这个经典的输出作为测试,为了遵循这个惯例,作为thrift菜鸟都不算的一员,决定跑一下“Hello world”正式进入菜鸟的行列. th ...
- python基础知识-11-函数装饰器
python其他知识目录 1.装饰器学习前热身准备 1.1装饰器简介 1.2装饰器热身分析 ) def func(): pass v1 = v2 = func #将函数名赋予一个变量,就和变量赋值是同 ...
- Homebrew -- 安装与使用
使用 React Native,必须安装的依赖有:Node.Watchman 和 React Native 命令行工具以及 Xcode. 推荐使用Homebrew来安装 Node 和 Watchman ...