已知一个$1—n$的排列$A=\{a_1,a_2,\cdots,a_n\}$,求它在所有排列中的字典序排名。
常用于将$n$个全排列映射到$n!$个自然数中。
求解这个问题的思路大概是下面这样的:
$(1)$ $A$的排名=字典序小于$A$的排列个数。所以只需要知道有多少个排列比$A$小就好了w
$(2)$ 我们按位考虑,第一位小于$a_1$的所有排列肯定比$A$小,这部分有$(a_{1}-1)\times (n-1)!$个。
$(3)$ 在第一个数等于$a_1$的所有排列中,第二位小于$a_2$的所有排列也肯定比$A$小。
那么这部分有$(a_{2}-1)\times (n-2)!$个对不对?
但是这个时候出现了一个问题:
如果$a_{1}<a_{2}$,那么第二位就不能再用$a_1$这个数了(因为是排列)。
所以应该有$(a_{2}-2)\times (n-2)!$个。
当然如果$a_{1}>a_{2}$就不需要额外$-1$了w
$(4)$ 现在我们把$(3)$的结论推广,
前$i-1$位与$A$相同且第$i$位小于$A$的排列,共有$(a_{i}-cnt_{i}-1)\times (n-i)!$个。
其中$cnt_i$表示$\{a_{1},a_{2},\cdots ,a_{i-1}\}$中小于$a_i$的个数。
显然所有这样的排列加起来就是比$A$小的排列总数(有序统计)。
$(5)$ 注意到$a_{i}-cnt_{i}-1$还等于后$\{a_{i},a_{i+1},\cdots ,a_{n}\}$中小于$a_i$的个数(因为是排列……)。
所以我们就得到了康托展开公式:
$Rank_{A}=b_{n}\times (n-1)!+b_{n-1}\times (n-2)!+\cdots +b_1 \times 0!$
其中$b_{i}$表示$a_i$在当前未出现的$a$中排在第几个。
关于实现,只需要按定义模拟即可。
代码:
inline int Cantor(){ int rank=0; for(int i=1;i<=N;i++){ int s=0; for(int j=i+1;j<=N;j++) s+=(A[j]<A[i]); rank+=s*jc[N-i]; } return rank; }