Note - 康托展开
1.用途#
康托展开可以用来求一个 的任意排列的排名。是一个很好的 方法。
2.算法介绍#
时间复杂度
普通的康托展开可以 的复杂度内求出排名,加上树状数组优化后则可以 。
实现
对于一个排列 , 统计 后面比它小的数字的个数 ,这 表示着有 各数在它前面,而在排列里面,每级别都是阶乘级的,所以答案就累加 。
用数学公式表示就是:
这样的负责度为 。
我们发现后面的 可以用树状数组来维护。于是时间复杂度降到了 。
代码
未优化
void contor(int a[]) {
int num = 0, ans = 0;
for (int i = 1; i < n; i++) {
for (int j = i + 1; j <= n; j++) {
if (a[j] < a[i]) {
num++;
}
}
ans += num * fac[n - i];
num = 0;
}
return ans + 1;
}
优化(附树状数组板子)
int c[maxn];
int lowbit(int x) {
return x & -x;
}
void add(int x, int k) {
for (; x <= n; x += lowbit(x)) {
c[x] += k;
}
}
int ask(int x) {
int ans = 0;
for (; x; x -= lowbit(x)) {
ans += c[x];
}
return ans;
}
void contor(int a[]) {
for (int i = 1; i <= n; i++) add(a[i], 1);
int ans = 0;
for (int i = 1; i < n; i++) {
int num = ask(a[i] - 1);
add(a[i], -1);
ans += sum * fac[n - i];
}
return ans + 1;
}
3.逆康托展开#
举个例子就很清晰啦~
引用自 康托展开 - OI WIKI
首先让 , 代表着有多少个排列比这个排列小。,有一个数小于它,所以第一位是 。
此时让排名减去 得到 , ,有 个数小于它,去掉已经存在的 ,这一位是 。
,,有一个数小于它,那么这一位就是 。
让 ,有一个数小于它,这一位是剩下来的第二位, ,剩下一位就是 。即 。
实际上我们得到了形如 有两个数小于它 这一结论,就知道它是当前第 个没有被选上的数,这里也可以用线段树维护,时间复杂度为 。
(未优化)
bool vis[maxn];
vector<int> reverse_contor(int x) {
x--;
vector<int> ans;
for (int i = 1; i <= n; i++) {
int t = n / fac[i];
for (int j = 1; j <= n; j++) {
if (!vis[j]) {
if (!t) {
vis[j] = true;
ans.push_back(j);
break;
}
t--;
}
}
x %= fac[i];
}
return ans;
}
作者: cqbzjyh
出处:https://www.cnblogs.com/cqbzjyh/p/15868809.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话