简介
康托展开用于计算某个字符串在所有字典序中的位置。常用于状态压缩和哈希。
公式
一个字符串A1,A2,…,An,假设都是数字0-9
这个字符串在所有字典序中的位置为:
B1*(n-1)!+B2*(n-2)!+...+Bn*(n-n)!
Bn表示这个字符后面比它小的字符的个数
以52134为例,B1=4,因为2134都比5小,B2=1,因为只有1比2小,...
这么做的理由是后面更小的字符可以换到当前位置上构成更小的字典序,后面的n个字符又有n!种排列方式。
优化
如果每次都要遍历一遍后面的字符,复杂度为O(n2),大部分的题目还是过不了,如果用树状数组进行优化,可以将复杂度降到O(nlogn)级别,可以应对绝大部分的题目。
例题
//AC代码
#include <bits/stdc++.h>
#define maxn 1000000 + 5
using namespace std;
int n;
long long num[maxn]; //记录输入的数字
long long C[maxn] = {0};
inline int lowbit(int x)
{
return x & (-x);
}
inline int presum(int x) //presum(i)表示比i小的数字个数
{
int res = 0;
while (x > 0)
{
res += C[x];
x -= lowbit(x);
}
return res;
}
inline void update(int x, int vaule)
{
while (x <= n)
{
C[x] += vaule;
x += lowbit(x);
}
}
int main()
{
long long f = 1, ans = 1;
scanf("%d", &n);
for (int i = n; i > 0; i--)
scanf("%lld", &num[i]);
update(num[1], 1);
//从最右端开始统计,一边统计一边建树
for (int i = 2; i <= n; i++)
{
ans += (presum(num[i]-1) * f) % 998244353;
ans %= 998244353;
update(num[i], 1); //每统计一个数,使小于等于num[i]的数加一
f = (f * i) % 998244353; //阶乘也是一样
}
printf("%d", ans);
return 0;
}
//这题要我们找出最小的步长,那么进行BFS即可
//BFS需要记录某个状态是否出现过,这题如果直接将排列化成数字,数量级将会达到10^9,空间肯定是不够用的
//如果将每种状态用康托展开,9个字符的排列一共只有9!种,大概36万,空间将大大减少
#include <bits/stdc++.h>
using namespace std;
struct condition //表示状态的结构体
{
int num[3][3];
int x, y; //记录0的位置
};
int len[362885] = {0}; //记录每个状态的步长
int fc[10] = {1, 1}; //阶乘打表
bool vis[362885] = {0}; //记录这个状态是否出现过
int cantor(condition &c) //将状态进行康托展开,离散化
{
int res = 0, cnt;
int tmp[9];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
tmp[i * 3 + j] = c.num[i][j];
}
for (int i = 0; i < 8; i++)
{
cnt = 0;
for (int j = i + 1; j < 9; j++)
{
if (tmp[j] < tmp[i])
cnt++;
}
res += cnt * fc[8 - i];
}
return res;
}
int main()
{
condition d{1,2,3,8,0,4,7,6,5, 2, 2}; //目标状态
for (int i = 2; i < 10; i++)
fc[i] = fc[i - 1] * i;
int des = cantor(d); //目标状态的康托编码
condition start;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
char c = getchar();
start.num[i][j] = c - '0';
if(c=='0')
{
start.x = i;
start.y = j;
}
}
}
vis[cantor(start)] = 1;
queue<condition> q;
q.push(start);
condition tmp;
condition nxt;
int x, y, last;
while (!q.empty()) //进行BFS搜索
{
tmp = q.front();
q.pop();
x = tmp.x;
y = tmp.y;
last = cantor(tmp);
if(last==des)
break;
if(last)
nxt = tmp;
if (x < 2)
{
swap(nxt.num[x][y], nxt.num[x + 1][y]);
int ct = cantor(nxt);
if (!vis[ct])
{
vis[ct] = 1;
len[ct] = len[last] + 1;
nxt.x = x + 1;
nxt.y = y;
q.push(nxt);
}
}
nxt = tmp;
if (x > 0)
{
swap(nxt.num[x][y], nxt.num[x - 1][y]);
int ct = cantor(nxt);
if (!vis[ct])
{
vis[ct] = 1;
len[ct] = len[last] + 1;
nxt.x = x - 1;
nxt.y = y;
q.push(nxt);
}
}
nxt = tmp;
if (y < 2)
{
swap(nxt.num[x][y], nxt.num[x][y + 1]);
int ct = cantor(nxt);
if (!vis[ct])
{
vis[ct] = 1;
len[ct] = len[last] + 1;
nxt.x = x;
nxt.y = y + 1;
q.push(nxt);
}
}
nxt = tmp;
if (y > 0)
{
swap(nxt.num[x][y], nxt.num[x][y - 1]);
int ct = cantor(nxt);
if (!vis[ct])
{
vis[ct] = 1;
len[ct] = len[last] + 1;
nxt.x = x;
nxt.y = y - 1;
q.push(nxt);
}
}
}
printf("%d", len[des]);
return 0;
}