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 ...
随机推荐
- CF 1138 E. Museums Tour
E. Museums Tour 链接 分析: 按时间建出分层图,每个点形如(u,t),表示u在在t个时刻的点,tarjan缩点.每个强连通分量中的点都能经过,然后DAG上dp. 代码: #includ ...
- linux编译安装aria2
一.安装aria2 [root@192-168-7-77 ~]# wget https://github.com/aria2/aria2/releases/download/release-1.33 ...
- 【译】Serverless架构 - 3
原文: https://martinfowler.com/articles/serverless.html 消息驱动型应用 后台数据处理服务是一个不同的例子. 你要写一个需要快速响应UI请求的以用户为 ...
- 动态权限<三>华为小米特殊机制
动态权限对于谷歌来说从android6.0引入,对于国内的rom来说,这个题目不是好的选择题.因为大多数时候由于使用群众的层次不同,有些人在乎隐私的泄露,而更多的人却并不关心,使用了动态权限,增加了用 ...
- Mkdir方法
新建目录或文件夹. 语法 MkDir 路径 所需的_路径_参数是一个字符串表达式,标识的目录或文件夹创建. _路径_可以包含驱动器. 如果未指定驱动器, MkDir当前的驱动器上创建新目录或文件夹. ...
- 高大上网站-CSS3总结1-图片2D处理以及BUG修复
高大上网站-CSS3总结1-图片2D处理以及BUG修复 一,前言: 现在的前端UI相对JS来说,重视并不够. 但是CSS3提供的新特性,将现在的网站赤裸裸的划分为两类:一类还在写着老旧样式,或者通过b ...
- 域名配置https
阿里可以一年的免费申请https证书 (1)域名->管理->免费开启SSL证书 (2)申请完.等待审核后就可以下载证书压缩包,包括key和pem两个文件 (3)在服务器的nginx目录下创 ...
- 【原创】标准HTTP请求工具类
以下是个人在项目开发过程中,总结的Http请求工具类,主要包括四种: 1.处理http POST请求[XML格式.无解压]: 2.处理http GET请求[XML格式.无解压]: 3.处理http P ...
- JUC——线程池
线程池本质的概念就是一堆线程一起完成一件事情. Executor package java.util.concurrent; public interface Executor { void exec ...
- Python的with语句(文件打开方式)
Python文件打开方式(with语句) python编程中对于文件的打开方式主要有以下两种: 1.利用直接性的open("","")函数:(举例说明) try ...