ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

康托展开

2022-09-03 19:31:11  阅读:255  来源: 互联网

标签:rt 排列 int sum tree num 展开 康托


lyin场切黑题太强了

首先康托展开是用来求一个全排列的排名的东西。复杂度\(O(n^2)\),树状数组可以到\(O(n\log n)\)。板子

简单说一下原理:首先一个长为\(n\)的排列数是\(n!\)没什么问题。所以我们可以对于每一位考虑有当前位之后有多少排列要比该排列小。

举个例子:\(3,1,4,2,5\)这个排列,第一位\(3\),比它小的有\(1,2,\)所以当\(1,2\)做开头的排列有\(2\times 4!\)种,累加进答案,顺便顺序考虑下一位。这是外层的扫描。

为什么这个是\(O(n^2)\)的呢?考虑第三位\(4\)。我们发现,前面两位是\(3,1,\)已经固定了,所以第三位比\(4\)小的就只有\(2\)。所以我们要顺序扫描每个比当前数小的有没有用过。

形式的一个公式是\(\sum_{i=1}^nsum_{a_i}\times(n-i)!\),其中\(sum_{a_i}=\sum_{j>i}a_j<a_i\)。也就是说,\(sum\)就是我们之前说的“还没用过的数” 的个数,而后面那个阶乘就是排列数。

我们发现这个可以用树状数组优化。初始化树状数组全为\(1\),每用过一个数更新\(-1\),每次查找\(sum\)直接查找比它小的数的个数,也就是还没用过的数的个数。

最后我们求出了比当前排列小的排列数,再加1就是排名。

上个代码。

int main(){
    scanf("%d",&n);jc[0]=1;
    for(int i=1;i<=n;i++){
    	scanf("%d",&a[i]);jc[i]=1ll*jc[i-1]*i%mod;//预处理阶乘 
    	update(i,1);//初始化 
	}
	for(int i=1;i<=n;i++){
		ans=(ans+1ll*query(a[i]-1)*jc[n-i]%mod)%mod;//直接按照定义累计答案 
		update(a[i],-1);//把a[i]删掉 
	}
	printf("%d",ans+1);
}

另一个东西:逆康托展开,就是知道排名求原排列。

首先显然排名\(-1\)。然后设这是第\(i\)个数,当前排名为\(x\),则比它小的排列数就是\(\lfloor\frac x{(n-i)!}\rfloor\),由此我们可以知道第\(i\)位是什么数字,然后让\(x\)减去这个排名,在下一位重复这个操作。

和原来差不多,我们建一棵权值线段树,每个点存储区间内未用过的数的个数,然后就可以直接查询了。

void pushup(int rt){
	tree[rt].num=tree[lson].num+tree[rson].num;
}
void build(int rt,int l,int r){
	tree[rt].l=l;tree[rt].r=r;
	if(l==r){
		tree[rt].num=1;return;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);build(rson,mid+1,r);
	pushup(rt);
}
int query(int rt,int pos){
	if(tree[rt].l==tree[rt].r){
		tree[rt].cnt=0;return tree[rt].l;//顺便更新 把这个数去掉 
	}
	int mid=(tree[rt].l+tree[rt].r)>>1,val=0;
	if(pos<=mid)val=query(lson,pos);
	else val=query(rson,pos);
	pushup(rt);
	return val; 
}
int main(){
	scanf("%d",&x);
	for(int i=1;i<=n;i++){
		int ret=x/jc[n-i];//当前排名 
		printf("%d",query(1,ret+1));//我们找到了比它小的个数所以+1就是原数 
		x-=ret;
	}
}

标签:rt,排列,int,sum,tree,num,展开,康托
来源: https://www.cnblogs.com/gtm1514/p/16653367.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有