数学/数论专题-学习笔记:康托展开
1.概述
康托展开,是全排列问题中常用的一种算法。
康托展开:已知一个 阶全排列 ,求出这是第几个全排列(按照字典序排序)。
2.实现
康托展开的公式:。本博客中约定:。
上式中, 表示比 小并且不在 位置前面的数的个数。
那么我们以 5 2 3 1 4
为例,模拟康托展开的步骤。
- 比
5
小的数有四个,其中有0
个在前面,因此 。这4
个数能够产生 个全排列。 - 比
2
小的数有一个,其中有0
个在前面,因此 。考虑到第1
个数已经确定,那么剩下这个数能够产生 个全排列。 - (此处省略若干字)
- 最后的结果为
为什么这么做?
比如第一位,我们可以发现:对于 1 2 3 4
这四个数而言,我们需要计算 5
不在第一位的全排列。根据排列组合的知识,结果为 。其余同理。
那么代码呢?其实还是比较好写的。
计算 我们可以使用权值线段树、set 等来解决。
当然我用的是 FHQ Treap,结果被卡常了。
用 FHQ Treap 的原因是我最近在学 FHQ Treap 所以用这个,其实权值线段树或 set 或树状数组会更简洁。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e6 + 10, P = 998244353;
int n, a[MAXN], cnt, root;
LL ans, f[MAXN];
struct node
{
int l, r, size, val, key;
}tree[MAXN];
int read()
{
int sum = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();}
return sum * fh;
}
int Make_Node(int val)
{
int now = ++cnt;
tree[now].size = 1;
tree[now].val = val;
return now;
}
void update(int x) {tree[x].size = tree[tree[x].l].size + tree[tree[x].r].size + 1;}
void split(int now, int val, int &x, int &y)
{
if (now == 0) x = y = 0;
else
{
if (tree[now].val <= val)
{
x = now;
split(tree[now].r, val, tree[now].r, y);
}
else
{
y = now;
split(tree[now].l, val, x, tree[now].l);
}
update(now);
}
}
int merge(int x, int y)
{
if (!x || !y) return x + y;
if (rand() & 1)
{
tree[x].r = merge(tree[x].r, y);
update(x); return x;
}
else
{
tree[y].l = merge(x, tree[y].l);
update(y); return y;
}
}
void Insert(int val)
{
int x, y; split(root, val - 1, x, y);
root = merge(merge(x, Make_Node(val)), y);
}
int Find(int val)
{
int x, y, ans;
split(root, val - 1, x, y);
ans = tree[x].size;
root = merge(x, y);
return ans;
}
int main()
{
srand(time(0));
n = read(); f[0] = 1; cnt = root = 0;
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1; i <= n; ++i) f[i] = f[i - 1] * i % P;
f[0] = 0;
for (int i = 1; i <= n; ++i)
{
Insert(a[i]); ans = (ans + ((LL)a[i] - 1 - Find(a[i])) * f[n - i]) % P;
// printf("%d/%d/%d\n", f[n - i], Find(a[i]), ans);//调试用
}
printf("%lld\n", ans + 1);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具