康托展开公式+代码

前言

本蒟刚刚学了基本的康托展开,特在此留下学习笔记。


康托展开是什么?

  康托展开就是求在一个特定长度的全排列中,一个特定的排列在全部排列中排第几个。举个例子:如长度为 3 3 3的全部排列为
1 , 2 , 3 1,2,3 1,2,3
1 , 3 , 2 1,3,2 1,3,2
2 , 1 , 3 2,1,3 2,1,3
2 , 3 , 1 2,3,1 2,3,1
3 , 1 , 2 3,1,2 3,1,2
3 , 2 , 1 3,2,1 3,2,1
所以 1 , 2 , 3 1,2,3 1,2,3就排第 1 1 1个, 1 , 3 , 2 1,3,2 1,3,2就排第 2 2 2 … … ……
康托展开就是能够快速求出这个“排名”的算法。

康托展开公式

a n s = x n ∗ ( n − 1 ) ! + x n − 1 ∗ ( n − 2 ) ! + … … + x 2 ∗ 1 ! + x 1 ∗ 0 ! + 1 ans=x_n*(n-1)!+x_{n-1}*(n-2)!+……+x_2*1!+x_1*0!+1 ans=xn(n1)!+xn1(n2)!++x21!+x10!+1
  其中 x i x_i xi为在 a 1 ∼ a i − 1 a_1 \sim a_{i-1} a1ai1当中有多少个数比小。也就是说, x i x_i xi表示有多少在他前面的数比它小。


说得我自己都 懵 懵 了,让我举个子来详细说明一下。
假如要求的排列为 3 , 1 , 2 3,1,2 3,1,2
因为 x i x_i xi表示有多少在他前面的数比它小,所以公式第一项 x n x_n xn a n a_n an前面比它大的数的数量,因为 a n a_n an 3 3 3 a n a_n an表示第一个数),所以说 x n x_n xn为第 n n n个位置前比 a n a_n an大的数的数量即 2 2 2 1 , 2 1,2 1,2都比 3 3 3小),然后再乘 ( n − 1 ) ! (n-1)! (n1)! 1 ∗ 2 = 2 1 * 2=2 12=2所以该公式第一项 x n ∗ ( n − 1 ) ! = 2 ∗ 2 = 4 x_n*(n-1)!=2*2=4 xn(n1)!=22=4
以此类推,第二项为 x n − 1 ∗ ( n − 2 ) ! = 0 ∗ 1 = 0 x_{n-1}*(n-2)!=0*1=0 xn1(n2)!=01=0,第三项也为 0 0 0。所以说 a n s = 4 + 0 + 0 + 1 = 5 ans=4+0+0+1=5 ans=4+0+0+1=5
至此,我们就求出了 3 , 1 , 2 3,1,2 3,1,2的“排名”为 5 5 5
下面是 1 , 2 , 3 1,2,3 1,2,3全排列的全部计算公式

序号数列计算公式
1 1 1 1 , 2 , 3 1,2,3 1,2,3 0 ∗ 2 ! + 0 ∗ 1 ! + 0 ∗ 0 ! + 1 = 1 0*2!+0*1!+0*0!+1=1 02!+01!+00!+1=1
2 2 2 1 , 3 , 2 1,3,2 1,3,2 0 ∗ 2 ! + 1 ∗ 1 ! + 0 ∗ 0 ! + 1 = 2 0*2!+1*1!+0*0!+1=2 02!+11!+00!+1=2
3 3 3 2 , 1 , 3 2,1,3 2,1,3 1 ∗ 2 ! + 0 ∗ 1 ! + 0 ∗ 0 ! + 1 = 3 1*2!+0*1!+0*0!+1=3 12!+01!+00!+1=3
4 4 4 2 , 3 , 1 2,3,1 2,3,1 1 ∗ 2 ! + 1 ∗ 1 ! + 0 ∗ 0 ! + 1 = 4 1*2!+1*1!+0*0!+1=4 12!+11!+00!+1=4
5 5 5 3 , 1 , 2 3,1,2 3,1,2 2 ∗ 2 ! + 0 ∗ 1 ! + 0 ∗ 0 ! + 1 = 5 2*2!+0*1!+0*0!+1=5 22!+01!+00!+1=5
6 6 6 3 , 2 , 1 3,2,1 3,2,1 2 ∗ 2 ! + 1 ∗ 1 ! + 0 ∗ 0 ! + 1 = 6 2*2!+1*1!+0*0!+1=6 22!+11!+00!+1=6

求康托展开的 C o d e Code Code

#include<bits/stdc++.h>
#define mod 998244353
#define sco 1000010
#define ll long long
#define rg register
#define getcha() (S==T&&(T=(S=fsr)+fread(fsr,1,1<<15,stdin),T==S)?EOF:*S++)
using namespace std;
ll n(0),ans(1),a[sco],jc[sco]={0,1};
char fsr[1<<15],*S=fsr,*T=fsr;
inline ll read() {
	rg ll r(0);
	rg char ch;
	while(ch=getcha(),ch>=58 || ch<=47);
	r=r*10+ch-48;
	while(ch=getcha(),ch<=57 && ch>=48)r=r*10+ch-48;
	return r;
}
signed main(){
	n=read();
	for(rg ll i=1;i<=n;++i)a[i]=read();
	for(rg ll i=2;i<=n;++i)jc[i]=jc[i-1]*i;
	for(rg ll i=1;i<n;++i){
		rg ll x=0;
		for(rg ll j=i+1;j<=n;++j){
			if(a[j]<a[i])++x;x%=mod;
		}
		ans=(ans+(x*jc[n-i]))%mod;
	}
	printf("%lld",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值