数论(7):康托展开&逆康托展开
康托展开可以用来求一个 \(1\sim n\) 的任意排列的排名。
什么是排列的排名?
把 \(1\sim n\) 的所有排列按字典序排序,这个排列的位次就是它的排名。
时间复杂度?
康托展开可以在 \(O(n^2)\) 的复杂度内求出一个排列的排名,在用到树状数组优化时可以做到 \(O(n\log n)\) 。
怎么实现?
因为排列是按字典序排名的,因此越靠前的数字优先级越高。也就是说如果两个排列的某一位之前的数字都相同,那么如果这一位如果不相同,就按这一位排序。
比如 \(4\) 的排列, \([2,3,1,4]<[2,3,4,1]\) ,因为在第 \(3\) 位出现不同,则 \([2,3,1,4]\) 的排名在 \([2,3,4,1]\) 前面。
举个栗子
我们知道长为 \(5\) 的排列 \([2,5,3,4,1]\) 大于以 \(1\) 为第一位的任何排列,以 \(1\) 为第一位的 \(5\) 的排列有 \(4!\) 种。这是非常好理解的。但是我们对第二位的 \(5\) 而言,它大于 第一位与这个排列相同的,而这一位比 \(5\) 小的 所有排列。不过我们要注意的是,这一位不仅要比 \(5\) 小,还要满足没有在当前排列的前面出现过,不然统计就重复了。因此这一位为 \(1,3\) 或 \(4\) ,第一位为 \(2\) 的所有排列都比它要小,数量为 \(3\times 3!\) 。
按照这样统计下去,答案就是 \(1+4!+3\times 3!+2!+1=46\) 。注意我们统计的是排名,因此最前面要 \(+1\) 。
注意到我们每次要用到 当前有多少个小于它的数还没有出现 ,这里用树状数组统计比它小的数出现过的次数就可以了。
原理总结:
(A[i]表示在位置i后比位置i上数小的数的个数)
\]
- 注意到我们每次要用到 当前有多少个小于它的数还没有出现
- 这里用树状数组统计比它小的数出现过的次数就可以了,可以优化到
O(nlogn)
代码
先预处理阶乘:
void init(){
fact[0] = 1;
for(int i = 2;i <= 9; ++i) fact[i] = fact[i - 1] * i;
//递推求阶乘
}
//或者直接打表
int fact[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
\(cantor\) 函数
int cantor(int a[],int n){
int ans = 0;
for(int i = 0;i < n; ++i){
int cnt = 0;
for(int j = i + ;j < n; ++j)++cnt;
// 找到a[i]是当前数列中未出现的数中第几小的
// 从1开始,即1-n的全排列
// 从0开始,就变成了0-n的全排列,记得变通
ans += cnt * fact[n - i - 1];
}
return ans + 1;//如果输出的是排名就要 + 1,如果是hash值可以直接返回 ans
}
逆康托展开
而逆康托展开相当于,反过来操作
因为排列的排名和排列是一一对应的,所以康托展开满足双射关系,是可逆的。可以通过类似上面的过程倒推回来。
如果我们知道一个排列的排名,就可以推出这个排列。因为 \(4!\) 是严格大于 \(3\times 3!+2\times 2!+1\times 1!\) 的,所以可以认为对于长度为 \(5\) 的排列,排名 \(x\) 除以 \(4!\) 向下取整就是有多少个数小于这个排列的第一位。
引用上面展开的例子
首先让 \(46-1=45\) , \(45\) 代表着有多少个排列比这个排列小。 \(\lfloor\frac {45}{4!}\rfloor=1\) ,有一个数小于它,所以第一位是 \(2\) 。
此时让排名减去 \(1\times 4!\) 得到 \(21\) , \(\lfloor\frac {21}{3!}\rfloor=3\) ,有 \(3\) 个数小于它,去掉已经存在的 \(2\) ,这一位是 \(5\) 。
\(21-3\times 3!=3\) , \(\lfloor\frac {3}{2!}\rfloor=1\) ,有一个数小于它,那么这一位就是 \(3\) 。
让 \(3-1\times 2!=1\) ,有一个数小于它,这一位是剩下来的第二位, \(4\) ,剩下一位就是 \(1\) 。即 \([2,5,3,4,1]\) 。
实际上我们得到了形如 有两个数小于它 这一结论,就知道它是当前第 \(3\) 个没有被选上的数,这里也可以用线段树维护,时间复杂度为 \(O(n\log n)\) 。
代码
vector<int> incantor(int x,int n){
x--;//得到从0开始的排名
vector<int> res(n); //保存数列答案
int cnt;
bool st[10]; //标记数组
memset(st,false,sizeof st);
for(int i = 0;i < n; ++i){
cnt = x / fact[n - i - 1]; // 比a[i]小且没有出现过的数的个数
x %= fact[n - i - 1]; //更新 x
for(int j = 1; j <= n; ++j){// 找到a[i],从1开始向后找
if(st[j]) continue; // 如果被标记过,就跳过
if(!cnt){ // 如果cnt == 0说明当前数是a[i]
st[j] = 1; //标记
res[i] = j; // 第i位是j
break;
}
cnt--; // 如果当前不是0,就继续往后找
}
}
return res;// 返回答案
}
数论(7):康托展开&逆康托展开的更多相关文章
- 康托展开&逆康托展开学习笔记
啊...好久没写了...可能是最后一篇学习笔记了吧 题目大意:给定序列求其在全排列中的排名&&给定排名求排列. 这就是康托展开&&逆康托展开要干的事了.下面依次介绍 一 ...
- 康拓展开 & 逆康拓展开 知识总结(树状数组优化)
康拓展开 : 康拓展开,难道他是要飞翔吗?哈哈,当然不是了,康拓具体是哪位大叔,我也不清楚,重要的是 我们需要用到它后面的展开,提到展开,与数学相关的,肯定是一个式子或者一个数进行分解,即 展开. 到 ...
- LightOJ1060 nth Permutation(不重复全排列+逆康托展开)
一年多前遇到差不多的题目http://acm.fafu.edu.cn/problem.php?id=1427. 一开始我还用搜索..后来那时意外找到一个不重复全排列的计算公式:M!/(N1!*N2!* ...
- nyoj 139——我排第几个|| nyoj 143——第几是谁? 康托展开与逆康托展开
讲解康托展开与逆康托展开.http://wenku.baidu.com/view/55ebccee4afe04a1b071deaf.html #include<bits/stdc++.h> ...
- 题解报告:NYOJ 题目143 第几是谁?(逆康托展开)
描述 现在有"abcdefghijkl”12个字符,将其按字典序排列,如果给出任意一种排列,我们能说出这个排列在所有的排列中是第几小的.但是现在我们给出它是第几小,需要你求出它所代表的序列. ...
- HDU1027 Ignatius and the Princess II( 逆康托展开 )
链接:传送门 题意:给出一个 n ,求 1 - n 全排列的第 m 个排列情况 思路:经典逆康托展开,需要注意的时要在原来逆康托展开的模板上改动一些地方. 分析:已知 1 <= M <= ...
- Codeforces-121C(逆康托展开)
题目大意: 给你两个数n,k求n的全排列的第k小,有多少满足如下条件的数: 首先定义一个幸运数字:只由4和7构成 对于排列p[i]满足i和p[i]都是幸运数字 思路: 对于n,k<=1e9 一眼 ...
- 康托展开+逆展开(Cantor expension)详解+优化
康托展开 引入 康托展开(Cantor expansion)用于将排列转换为字典序的索引(逆展开则相反) 百度百科 维基百科 方法 假设我们要求排列 5 2 4 1 3 的字典序索引 逐位处理: 第一 ...
- CDOJ 485 UESTC 485 Game (八数码变形,映射,逆cantor展开)
题意:八数码,但是转移的方式是转动,一共十二种,有多组询问,初态唯一,终态不唯一. 题解:初态唯一,那么可以预处理出012345678的所有转移情况,然后将初态对012345678做一个映射,再枚举一 ...
- hdu 1027 Ignatius and the Princess II(正、逆康托)
题意: 给N和M. 输出1,2,...,N的第M大全排列. 思路: 将M逆康托,求出a1,a2,...aN. 看代码. 代码: int const MAXM=10000; int fac[15]; i ...
随机推荐
- 【Javaweb】html frame标签的使用 | 导航栏右侧内容的实现
问题的产生: 是我和同伴做了一个导航栏,但是我们不知道怎么实现右侧内容的切换 然后我们查了很多资料,但是有一些是垂直的,但是就如图可见,我们是水平的,那么怎么实现水平切换呢? 上网看到了frame标签 ...
- 黑客玩具入门——5、继续Metasploit
1.利用FTP漏洞并植入后门 实验靶机:Metasploitable2. 实践: 使用nmap扫描目标靶机 nmap -sV xxx.xxx.xxx.xxx(目标ip) 生成linux系统后门 msf ...
- java当中组件和窗口的相容问题(里面包含了这些方法的作用)
窗口也称为容器,在书中大部分称为容器,这里主要讲的是底层容器. JFrame():创建一个无标签的窗口. JFrame(String s):创建标题为s的窗口 setBounds(int a,int ...
- 开发AI量化策略所遇到的坑
AI只是工具,想要驾驭AI还得自身有点功底,不然反而会被工具所害,甚至从信仰AI变为抵制AI.本文简单介绍开发AI量化选股策略中所遇到的各种坑,希望大家有所收获,少走弯路. 本文为BigQuant用户 ...
- Kernel Memory 入门系列:自定义处理流程
Kernel Memory 入门系列:自定义处理流程 在整个文档预处理的流程中,涉及到很多的处理步骤,例如:文本提取,文本分片,向量化和存储.这些步骤是Kernel Memory中的默认提供的处理方法 ...
- 笔记本安装linux
下载 桌面版 Ubuntu 镜像 服务器版 Ubuntu 镜像 使用 Balena Etcher 制作系统安装盘 (1)官方网站下载: 点我下载 (2)下载完毕软件之后,打开软件,选择我们下载好的系统 ...
- Kiractf
信息收集 主机发现和端口扫描只开放了80的web服务 WEB打点 访问首页有文件上传,肯定可以利用一波.language那个页面甚至文件包含都写脸上了. root@Lockly tmp/ki ...
- MySQL|mysql-索引
1.索引是什么 1.1索引简介 索引是表的目录,是数据库中专门用于帮助用户快速查询数据的一种数据结构.类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,以及快速定位查询数据.对于索引 ...
- 笔记本为什么不出可升级CPU的,用台式CPU不行吗?
我十几年年前的thinkpad 通过更换cpu复活了:联想ThinkPad E430c i3变i7:笔记本电脑CPU升级思路-CPU参数 为什么现在的市面上可以更换cpu的笔记本非常稀少呢? inte ...
- VSCode C++开发环境配置:CMake 调试配置 launch.json
相关内容 VSCode C++开发环境配置: LLVM clang clangd 安装 cmake sudo apt install cmake 安装 VSCode 插件 CMake CMakeToo ...