问题是这样的,给出一个 \(1\sim N\) 全排列,求在所有 \(1\sim N\) 全排列中的排名。
如果暴力枚举 \(1\sim N\) 每一个全排列,再 \(O(N)\) 判断是否符合,时间复杂度轻松爆炸,这时候便需要康托展开。
首先把这个 \(1\sim N\) 的全排列记为 \(A\),他从左往右的第 \(i\) 位记为 \(A_i\),问题就是有多少个 \(1\sim N\) 的全排列小于它,最后再 \(+1\)。
考虑第一位,任何以小于 \(A_1\) 开头的全排列肯定都小于 \(A\),令小于 \(A_1\) 的有 \(sum\) 个,这样的全排列就有 \(sum\times (N-1)!\) 个。
但是还没有完,因为等于 \(A_1\) 也有可能小于 \(A\),所以接下来就要考虑下一位。
令小于 \(A_2\) 的有 \(sum\) 个,但是有可能 \(A_1\) 小于 \(A_2\),如果把这个算进来就不符合全排列的定义,所以要删掉所有在前面已经出现过且小于 \(A_i\) 的数,而这样的全排列有 \(sum\times (N-2)!\) 个。
以此类推,答案自然就出来了,时间复杂度为 \(O(N^2)\)。
如果数据比较大,这样还是过不了,转化一下,第 \(i\) 项的 \(sum\) 的求法就是 \(\sum_{j=i+1}^{N}(A_j<A_i)\)。再转化一下就是 \(j>i\) 且 \(A_j<A_i\),典型二位偏序,可以树状数组优化。
冷门算法,不知道会不会考。
int ans=0;//表示最后的排名
for(int i=1;i<=n;i++){
int x;scanf("%lld",&x);
T.add(x,1);//树状数组T
ans=(ans+(x-T.ask(x))*jc[n-i]%MOD)%MOD;//一般这种答案比较大,题目可能要求取模
}
printf("%lld",ans+1);//别忘了+1
经典题目:
【模板】康托展开 洛谷 (模板)
标签:小于,排列,sum,讲解,展开,sim,康托 From: https://www.cnblogs.com/HEIMOFA/p/17663664.html