Algorithm study notes: Cantor expansion & inverse Cantor expansion
1 Overview
Cantor Expansion & Inverse Cantor Expansion are two commonly used algorithms in full permutation problems.
Cantor Expansion: Knowing a nnn order full arrayaaa , find out which full permutation is this (sorted in lexicographic order).
Inverse Cantor Expansion: Knowing a nnn order full arrayaaa ranking obtained in this whole arrangement.
It can be found that these two operations are simply twin brothers.
2. Realize
2.1 Cantor expansion
Cantor’s expansion formula: a 1 × (n − 1)! + A 2 × (n − 2)! +... + An − 1 × 1! + An × 0! + 1 a_1 \times (n-1 )! + a_2 \times (n-2)! +...+ a_{n-1} \times 1! + a_n \times 0! + 1a1×(n−1)!+a2×(n−2)!+...+an−1×1!+an×0!+1 . Conventions in this blog:0! = 0 0! = 00!=0。
In the above formula, ai a_iaiMeans better than ai a_iaiSmall and not in iiThe number of numbers in front of the i position.
So we have to 5 2 3 1 4
, for example, expanded simulation step Cantor.
- Than the
5
number of small four, which0
at the front, so a 1 = 4 a_1 = 4a1=4 . This4
number can be generated4 × 4! 4 \ times 44×4 ! Full arrangement. - Than the
2
number of a small, which has0
at the front, so a 2 = 1 a_2 = 1a2=1 . Taking into account of1
the number has been determined, the remaining number capable of producing the1 × 3! 1 \ times 31×3 ! Full arrangement. - (Some words are omitted here)
- The final result is 4 × 4! + 1 × 3! + 1 × 2! + 0 × 1! + 0 × 0! + 1 = 105 4 \times 4! + 1 \times 3! + 1 \times 2! + 0 \times 1! + 0 \times 0! + 1 = 1054×4!+1×3!+1×2!+0×1!+0×0!+1=105
Why do you do this?
Such as the first one, we can find: For 1 2 3 4
these four numbers, we need to calculate 5
not the whole arrangement first. According to the knowledge of permutation and combination, the result is 4 × 4 × 3 × 2 × 1 = 4 × 4! 4 \times 4 \times 3 \times 2 \times 1 = 4 \times 4!4×4×3×2×1=4×4 ! . The rest is the same.
What about the code? In fact, it is easier to write.
Calculation ai a_iai We can use the weighted line segment tree, set, etc. to solve it.
Of course I used FHQ Treap, but it got stuck.
Never learned FHQ Treap students can take a look at this blog , there are very detailed explanation. Of course, you can also use other implementations.
Code:
#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;
}
2.2 Inverse Cantor expansion
Because the author is too weak, he did not learn.