康托展开&逆康托展开

康托展开&逆康托展开

1.算法分析

康托展开可以求一个序列是第几个排列,即求得[2, 1, 3]是第3个排列

逆康托展开可以求得第k个排列是多少,即求得第3个排列为[2, 1, 3]

基于这个性质,可以使用康托展开把一个序列做哈希,映射为一个数字。

1.1 康托展开

康托展开公式:当前排列的的排名为: r a n k = a n ∗ ( n − 1 ) ! + a n − 1 ∗ ( n − 2 ) ! + . . . + a 1 ∗ 0 + 1 rank = a_n*(n - 1)! + a_{n - 1}*(n - 2)! + ... + a_1 * 0 + 1 rank=an(n1)!+an1(n2)!+...+a10+1

其中,其中a[i]表示第i个数在未出现的数中排第几。

朴素的想法为当遍历到当前数字a[i]时,向前扫描,看有多少个还没有用过的数字。这样为 O ( n 2 ) O(n^2) O(n2)

但是可以记使用过为0,没有使用过为1,这样可以使用树状数组维护前缀和,即可知道当前数字在没有用过的数字中排第几。

1.2 逆康托展开

逆康托展开是康托展开的逆过程,倒着从(n-1)!的阶乘开始往1!做除法,然后按照康托展开的逻辑求即可。

以[4,2,5,1,3]为例:

  1. 82/(4!),商为3,余数为10:那么要找没有出现过的第3+1=4个数字,即4.
  2. 10/(3!),商为1,余数为4:那么要找没有出现过的第1+1=2个数字,即2.
  3. 4/(2!),商为2,余数为0:那么要找没有出现过的第2+1=3个数字,即5.
  4. 0/(1!),商为0,余数为0:那么要找没有出现过的第0+1=1个数字,即1.
  5. 最后将未填充的数3填到最后一位

基于上述思想可以发现核心问题就找没有出现过的第k个数字,朴素思想就是用二分+树状数组实现。但是利用

2.模板

#include <bits/stdc++.h>

using namespace std;

int const N = 2e6 + 10, MOD = 998244353;
typedef long long LL;

LL fact[N];
int c[N], n, m, a[N];  // n为序列长度,m为大于n的第一个2的幂次方,注意这里c数组空间要开双倍

int lowbit(int x) {
   
    return x & (-x);
}

// 单点修改
void add(int x, int y) {
   
    for (int i = x; i <= m; i += lowbit(i)) c
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值