UVA11525 Permutation 题解
Post time: 2020-01-23 14:34:55
今天来分享UVA11525这道题。题目链接 LINK
这道题的本质是一个叫做康托展开的东西。就是把几个数字的每种排列都映射成一个数字(即为在全排列当中字典序排序后的序号),可以理解成一种哈希。
题目当中已经给出了康托展开的式子。这题其实是一个逆康托展开的过程,因为题目已经完成了寻找S1-Sk的过程,我们只需要用一种高效的数据结构来完成寻找即可。
运用权值线段树可以解决此类问题。每个节点的权值是这个节点的[l,r]范围内没有用的数(叶子结点即为1)的个数。第i次查询的时候寻找排名第Si的数:
点击查看代码
#include<iostream>
#include<cstdio>
#define N (500000+21)
//define一定一定要记得加括号
using namespace std;
struct SegmentTree{int l,r,cnt;}t[N*4];//线段树数组要开四倍
int T,n,x;
//下面是线段树模板,基本和区间加差不多,就是注意查询操作类似于平衡树找第k名
void build(int p,int l,int r){
t[p].l=l,t[p].r=r;
if(l==r){
t[p].cnt=1;
return;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
}
int modify(int p,int v){
if(t[p].l==t[p].r){
t[p].cnt=0;//这里随着查询也改变了数值
return t[p].l;
}
int res=0;
if(v<=t[p*2].cnt) res=modify(p*2,v);//写p<<1更好
else res=modify(p*2+1,v-t[p*2].cnt);//这里本人码风不好,写p<<1|1更好
t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
return res;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
build(1,1,n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
printf("%d",modify(1,x+1));//因为从0开始所以要把x加1
if(i!=n) printf(" ");
}
printf("\n");
}
return 0;
}