Algorithm study notes: Cantor expansion & inverse Cantor expansion

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

example

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×(n1)!+a2×(n2)!+...+an1×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.

  1. Than the 5number of small four, which 0at the front, so a 1 = 4 a_1 = 4a1=4 . This4number can be generated4 × 4! 4 \ times 44×4 ! Full arrangement.
  2. Than the 2number of a small, which has 0at the front, so a 2 = 1 a_2 = 1a2=1 . Taking into account of1the number has been determined, the remaining number capable of producing the1 × 3! 1 \ times 31×3 ! Full arrangement.
  3. (Some words are omitted here)
  4. 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 4these four numbers, we need to calculate 5not 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.

Guess you like

Origin blog.csdn.net/BWzhuzehao/article/details/112744602