题面

康托展开:

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

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

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

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

  1. long long cantor()
  2. {
  3. long long ans=;
  4. inc(,n,){
  5. long long cnt=;
  6. long long num=,sum=;
  7. for(int j=i+;j<=n;j++){
  8. if(a[j]<a[i]) ++cnt;
  9. sum=sum*num%p;
  10. ++num;
  11. }
  12. ans=(ans+cnt*sum%p)%p;
  13. }
  14. return ans%p;
  15. }

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

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

逆康托展开:

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

举例子:

在(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。

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

但是......

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

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

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

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

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

  1. #include <bits/stdc++.h>
  2. #define p 998244353
  3. #define inc(a,b,c) for(register int i=a;i<=b;i+=c)
  4. using namespace std;
  5. int n;
  6. int a[1000010],c[1000010];
  7. long long pre[1000010];
  8. int lowbit(int x)
  9. {
  10. return x&(-x);
  11. }
  12. int query(int x)
  13. {
  14. int res=0;
  15. while(x>0){
  16. res+=c[x];
  17. x-=lowbit(x);
  18. }
  19. return res;
  20. }
  21. void add(int x)
  22. {
  23. while(x<=n){
  24. c[x]+=1;
  25. x+=lowbit(x);
  26. }
  27. }
  28. long long cantor()
  29. {
  30. long long ans=0;
  31. for(register int i=n;i>=1;i--){
  32. long long ask=query(a[i]);
  33. add(a[i]);
  34. long long tmp=pre[n-i];
  35. ans=(ans+tmp*ask%p)%p;
  36. }
  37. return ans%p;
  38. }
  39. int main()
  40. {
  41. cin>>n;
  42. pre[0]=1;
  43. inc(1,n,1){
  44. pre[i]=pre[i-1]*i%p;
  45. }
  46. inc(1,n,1){
  47. scanf("%d",&a[i]);
  48. }
  49. cout<<cantor()+1;
  50.  
  51. }

附:公式的证明

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

还是用例子来说明吧,更易于理解,考察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. CSS3做出条纹大背景

    ㈠实现不等宽背景条纹   实现如上图所示的效果,代码如下: <!DOCTYPE html> <html lang="en"> <head> &l ...

  2. Centos 由字符界面 init 3 切换图形界面 init 5

    Centos6 和 Centos7 由字符界面切换成 图形界面方法不同,下面分别介绍. 一.Centos6 切换方法 yum -y install xorg* yum -y groupinstall ...

  3. 计算机网络(八),TCP的滑动窗口

    目录 1.RTT和RTO 2.TCP使用滑动窗口做流量控制与乱序重排 3.滑动窗口的基本原理 八.TCP的滑动窗口 TCP头部中的窗口字段:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端 ...

  4. dp周训练 状态压缩

    题目链接:题意:给你一个10*10的矩阵,每到一个格子中都要拿一个0-9的数值,求从矩阵左上方走到右下方且必须0-9都经过,拿的数值和最小是多少: #include <iostream> ...

  5. Linux命令-文本编辑(一)

    Linux命令-文本编辑(一) Linux col命令 Linux col命令用于过滤控制字符. 在许多UNIX说明文件里,都有RLF控制字符.当我们运用shell特殊字符">&quo ...

  6. springCloud 搭建eureka服务之天坑

    这里我是采用gradle来管理jar包的. 1.使用idea创建一个gradle项目. 2.编辑settings.gradle文件 rootProject.name = 'jtm' //include ...

  7. Redis的一点笔记

    Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 优势: 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s . 丰富 ...

  8. linux 系统的 cache 过大,解决方案

    linux buff/cache过大,清理脚本 2018年06月20日 13:44:53 taozhe666 阅读数:6500   三条指令: sync echo 1 > /proc/sys/v ...

  9. 通过tar包解压安装docker

    以下步骤在centos7和debian10中验证通过 1.通过官网下载安装包 wget https://download.docker.com/linux/static/stable/x86_64/d ...

  10. 方差分析(python代码实现)

    python机器学习-乳腺癌细胞挖掘(欢迎关注博主主页,学习python视频资源,还有大量免费python经典文章) https://study.163.com/course/introduction ...