感谢ZLY告诉我这个知识点
附上比较不错的模板
https://www.cnblogs.com/Howe-Young/p/4348777.html
例题:
http://nyoj.top/problem/139(康托展开)
http://nyoj.top/problem/143(康托逆展开)
此题是理解康托展开非常好的例子
就该题而言会不会康托展开都会想到用排列组合计算比该序列小的序列有多少个
我也认为这就是康托展开的原理:
记录当前序列为该序列字典序中的第N大(等同于映射为整形值),由于其序列唯一性,则可通过序列编号逆运算出原序列。
由于是自己推的, 不保证正确性,待我再深入学习学习再修改
typedef long long ll;
const int MAXN = 1e6 + 10;
int cnt[MAXN] = {0};
int main()
{
int n;
cin>>n;
while(n--)
{
string rs;
cin>>rs;
ll ans = 0;
for(int i = 0; i < rs.length(); i++)
{
int rsum = 0;
for(int j = i + 1; j < rs.length(); j++)
{
if(rs[j] < rs[i])
rsum++;
}
cnt[i] = rsum;
}
for(ll i = rs.length() - 2, x = 1, rs = 1; i >= 0; i--, rs++)
{
x *= rs;
ans += (cnt[i] * x);
}
cout<<ans + 1<<'
';
}
}
逆展开:
预处理fac阶乘数组
从尾部依次除fac[len - i](i from 1 to len) 除数为比该数小的数的个数
然后n方暴力查找 去掉该数 并修改其后数组数量 -1 动态更新,保证解的唯一性
用过我直接标-1 减了还减 反正也匹配不上 比较方便
typedef long long ll;
const int MAXN = 1e3 + 10;
char arr[MAXN] = "abcdefghijkl";
int fac[MAXN] = {1};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("D://test.in", "r", stdin);
//freopen("D://test.out", "w", stdout);
for(ll x = 1, y = 1; y < 15; y++)
{
fac[y] = x * y;
x = x * y;
//cout<<fac[y]<<' ';
}
int cnt[20] = {0};
int n;
cin>>n;
while(n--)
{
vector <char> ans;
for(int i = 0; i < 12; i++)
{
cnt[i] = i;
}
ll rx;
cin>>rx;
--rx;
for(int i = 11; i >= 0; i--)
{
int rcnt = rx / fac[i];
rx %= fac[i];
for(int j = 0; j < 12; j++)
{
if(cnt[j] == rcnt)
{
cnt[j] = -1;
ans.push_back(j + 'a');
for(int k = j + 1; k < 12; k++)
{
--cnt[k];
}
break;
}
}
}
for(char x : ans)
cout<<x;
cout<<'
';
}
return 0;
}