前言
本蒟刚刚学了基本的康托展开,特在此留下学习笔记。
康托展开是什么?
康托展开就是求在一个特定长度的全排列中,一个特定的排列在全部排列中排第几个。举个例子:如长度为
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∗(n−1)!+xn−1∗(n−2)!+……+x2∗1!+x1∗0!+1
其中
x
i
x_i
xi为在
a
1
∼
a
i
−
1
a_1 \sim a_{i-1}
a1∼ai−1当中有多少个数比小。也就是说,
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)!
(n−1)!即
1
∗
2
=
2
1 * 2=2
1∗2=2所以该公式第一项
x
n
∗
(
n
−
1
)
!
=
2
∗
2
=
4
x_n*(n-1)!=2*2=4
xn∗(n−1)!=2∗2=4。
以此类推,第二项为
x
n
−
1
∗
(
n
−
2
)
!
=
0
∗
1
=
0
x_{n-1}*(n-2)!=0*1=0
xn−1∗(n−2)!=0∗1=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 0∗2!+0∗1!+0∗0!+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 0∗2!+1∗1!+0∗0!+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 1∗2!+0∗1!+0∗0!+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 1∗2!+1∗1!+0∗0!+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 2∗2!+0∗1!+0∗0!+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 2∗2!+1∗1!+0∗0!+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;
}