cunzai_zsy0531

关注我

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;
}
posted @ 2022-04-21 11:45  cunzai_zsy0531  阅读(27)  评论(0编辑  收藏  举报