题面

康托展开:

康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的名次,因此是可逆的。

X = A[0] * (n-1)! + A[1] * (n-2)! + … + A[n-1] * 0!

A[i] 指的是位于位置i后面的数小于A[i]值的个数,后面乘的就是后面还有多少个数的阶乘

这个算出来的数康拖展开值,是在所有排列次序 - 1的值,因此X+1即为在全排列中的次序

long long cantor()
{
long long ans=;
inc(,n,){
long long cnt=;
long long num=,sum=;
for(int j=i+;j<=n;j++){
if(a[j]<a[i]) ++cnt;
sum=sum*num%p;
++num;
}
ans=(ans+cnt*sum%p)%p;
}
return ans%p;
}

问:虽然知道了康托展开的方法,但怎样知道排名求排列呢?

答:那就使用逆康托展开!;

逆康托展开:

前面已经说到康拖展开是从序列到自然数的映射且是可逆的,那么逆康拖展开便是从自然数到序列的映射。

举例子:

在(1,2,3,4,5) 给出61可以算出起排列组合为34152
具体过程如下:
用 61 / 4! = 2余13,说明 ,说明比首位小的数有2个,所以首位为3。
用 13 / 3! = 2余1,说明 ,说明在第二位之后小于第二位的数有2个,所以第二位为4。
用 1 / 2! = 0余1,说明 ,说明在第三位之后没有小于第三位的数,所以第三位为1。
用 1 / 1! = 1余0,说明 ,说明在第二位之后小于第四位的数有1个,所以第四位为5。

static const int FAC[] = {, , , , , , , , , };   // 阶乘预处理
void decantor(int x, int n)
{
vector<int> v; // 存放当前可选数
vector<int> a; // 所求排列组合
for(int i=;i<=n;i++)
v.push_back(i);
for(int i=m;i>=;i--)
{
int r = x % FAC[i-];
int t = x / FAC[i-];
x = r;
sort(v.begin(),v.end());// 从小到大排序
a.push_back(v[t]); // 剩余数里第t+1个数为当前位
v.erase(v.begin()+t); // 移除选做当前位的数
}
}

但是......

你会发现,这样的时间复杂度是n^2的,我们无法接受这么暴力的算法;即使比枚举快乐很多;

那么,我们总不能整个ex康托展开,所以开始考虑优化;

从阶乘上入手?还是不够快。那么剩余优化的仅仅剩一个地方:统计小于a[i]个数.

这时我们会想到我们可爱的朋友:树状数组。

nlogn的复杂度,开心愉悦的AC掉了它;

#include <bits/stdc++.h>
#define p 998244353
#define inc(a,b,c) for(register int i=a;i<=b;i+=c)
using namespace std;
int n;
int a[1000010],c[1000010];
long long pre[1000010];
int lowbit(int x)
{
return x&(-x);
}
int query(int x)
{
int res=0;
while(x>0){
res+=c[x];
x-=lowbit(x);
}
return res;
}
void add(int x)
{
while(x<=n){
c[x]+=1;
x+=lowbit(x);
}
}
long long cantor()
{
long long ans=0;
for(register int i=n;i>=1;i--){
long long ask=query(a[i]);
add(a[i]);
long long tmp=pre[n-i];
ans=(ans+tmp*ask%p)%p;
}
return ans%p;
}
int main()
{
cin>>n;
pre[0]=1;
inc(1,n,1){
pre[i]=pre[i-1]*i%p;
}
inc(1,n,1){
scanf("%d",&a[i]);
}
cout<<cantor()+1; }

附:公式的证明

某一全排列序列的编号即等于排在它前面的全排列的个数,而由编号定义可知,其前面的全排列组成数都小于该全排列组成的数。

还是用例子来说明吧,更易于理解,考察3214,我们要求出比它小的全排列的个数,可以这样计算:

1. 千位取1或2,后三位由剩下的3个元素全排列,共2*3!种;

2. 千位取3,百位取小于2的元素,只能为1,后两位由剩下的2个元素全排列,共1*2!种;

3. 千位取3,百位取2,十位取小于1的元素,不存在;

4. 最后一项一定为0;

LOJ167 康托展开 题解的更多相关文章

  1. HDU1043 Eight(八数码:逆向BFS打表+康托展开)题解

    Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  2. 题解报告:NYOJ 题目139 我排第几个(康托展开)

    描述 现在有"abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的? 输入 第一行有一个整数n(0<n<=1 ...

  3. 题解报告:NYOJ 题目143 第几是谁?(逆康托展开)

    描述 现在有"abcdefghijkl”12个字符,将其按字典序排列,如果给出任意一种排列,我们能说出这个排列在所有的排列中是第几小的.但是现在我们给出它是第几小,需要你求出它所代表的序列. ...

  4. 题解 P5367 【【模板】康托展开】

    P5367 [模板]康托展开 感觉这题难度大概在绿题到蓝题之间qwq 一.洛谷日报[yummy]浅谈康托展开 如我想知道321是{1,2,3}中第几个小的数可以这样考虑 : 第一位是3,当第一位的数小 ...

  5. 双向广搜+hash+康托展开 codevs 1225 八数码难题

    codevs 1225 八数码难题  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond   题目描述 Description Yours和zero在研究A*启 ...

  6. hdu 1430(BFS+康托展开+映射+输出路径)

    魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...

  7. 【BZOJ】3301: [USACO2011 Feb] Cow Line(康托展开)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3301 其实这一题很早就a过了,但是那时候看题解写完也是似懂非懂的.... 听zyf神犇说是康托展开, ...

  8. hdu3567 八数码2(康托展开+多次bfs+预处理)

    Eight II Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 130000/65536 K (Java/Others)Total S ...

  9. HDU - 1430 魔板 【BFS + 康托展开 + 哈希】

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1430 思路 我刚开始 想到的 就是 康托展开 但是这个题目是 多组输入 即使用 康托展开 也是会T的 ...

随机推荐

  1. 顺序查找(Sequential Search)

    1.定义 顺序查找又叫线性查找,是最基本的查找技术. 2.基本思想 从表的一端开始(第一个或最后一个记录),顺序扫描线性表,依次将扫描到的结点关键宇和给定值K相比较.若当前扫描到的结点关键字与K相等, ...

  2. py脚本修改后自动重启

    在用socket.io, pika之类启动一个脚本死循环做server或者client的时候: 1脚本被编辑之后,是不会自动重启 2当代码报错的时候,会立即退出, 只能手动重新运行 python ap ...

  3. 'vue' 不是内部或外部命令,也不是可运行的程序 或批处理文件

    解决方案:找到npm i xxx -g 下载后存放的路径,将路径添加到环境变量中,即可.1.npm config list 查看一下npm 的配置信息 2.打开路径看看里面的命令.window用户wi ...

  4. idea maven projects 工具栏按钮的作用

    1.Execute Maven Goal  弹出可执行的 Maven 命令的输入框.有些情况下我们需要通过书写某些执行命令来构建项目,就可以通过此按钮 2.Toggle Offline Mode 英文 ...

  5. POJ3233 [C - Matrix Power Series] 矩阵乘法

    解题思路 题目里要求\(\sum_{i=1}^kA^i\),我们不妨再加上一个单位矩阵,求\(\sum_{i=0}^kA^i\).然后我们发现这个式子可以写成这样的形式:\(A(A(A...)+E)+ ...

  6. App可视化埋点技术原理大揭秘

    一.背景 运营者能够对用户行为进行分析的前提,是对大量数据的掌握.在以往,这个数据通常是由开发者在控件点击.页面等事件中,一行行地编写埋点代码来完成数据收集的.然而传统的操作模式每当升级改版时,开发和 ...

  7. Xcode 4.1实用小工具:模拟网络连接和带宽

    暂无评论 适用于Mac OS X Lion的开发套件Xcode 4.1中,有个新鲜的小工具叫做Network Link Conditioner(网络连接调节器),是一款具有高度可定制性的辅助工具,让用 ...

  8. Java并发编程的艺术笔记(四)——ThreadLocal的使用

    ThreadLocal,即线程变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上 ...

  9. 链表栈C语言实现

    #ifndef LINKSTACK_H_INCLUDED #define LINKSTACK_H_INCLUDED #include <stdlib.h> #include <std ...

  10. hdjs---1、hdjs爬坑杂记

    hdjs---1.hdjs爬坑杂记 一.总结 一句话总结: 对hdjs这种文档和完善都不是很好的插件,应该先在项目的空页面试,成功后再用到用了框架的项目中 1.hdjs4.0.18引入select2? ...