LOJ167 康托展开 题解
康托展开:
康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的名次,因此是可逆的。
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 康托展开 题解的更多相关文章
- HDU1043 Eight(八数码:逆向BFS打表+康托展开)题解
Eight Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
- 题解报告:NYOJ 题目139 我排第几个(康托展开)
描述 现在有"abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的? 输入 第一行有一个整数n(0<n<=1 ...
- 题解报告:NYOJ 题目143 第几是谁?(逆康托展开)
描述 现在有"abcdefghijkl”12个字符,将其按字典序排列,如果给出任意一种排列,我们能说出这个排列在所有的排列中是第几小的.但是现在我们给出它是第几小,需要你求出它所代表的序列. ...
- 题解 P5367 【【模板】康托展开】
P5367 [模板]康托展开 感觉这题难度大概在绿题到蓝题之间qwq 一.洛谷日报[yummy]浅谈康托展开 如我想知道321是{1,2,3}中第几个小的数可以这样考虑 : 第一位是3,当第一位的数小 ...
- 双向广搜+hash+康托展开 codevs 1225 八数码难题
codevs 1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Yours和zero在研究A*启 ...
- hdu 1430(BFS+康托展开+映射+输出路径)
魔板 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...
- 【BZOJ】3301: [USACO2011 Feb] Cow Line(康托展开)
http://www.lydsy.com/JudgeOnline/problem.php?id=3301 其实这一题很早就a过了,但是那时候看题解写完也是似懂非懂的.... 听zyf神犇说是康托展开, ...
- hdu3567 八数码2(康托展开+多次bfs+预处理)
Eight II Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 130000/65536 K (Java/Others)Total S ...
- HDU - 1430 魔板 【BFS + 康托展开 + 哈希】
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1430 思路 我刚开始 想到的 就是 康托展开 但是这个题目是 多组输入 即使用 康托展开 也是会T的 ...
随机推荐
- Beyong Compare 2.4.10刷新剩余日期bat批处理文件
@echo off % start )(window.close)&&exit REG DELETE "HKCU\Software\Scooter Software\Beyo ...
- IE8 浏览器文本模式变为杂项(quirks)页面变形的解决方法
IE8 浏览器文本模式(document.documentMode)变为杂项(quirks)页面变形的解决方法. 正常情况下 document.documentMode 输出为8,而变为杂项之后doc ...
- 探讨MySQL的各类锁
参考文档:http://blog.csdn.net/soonfly/article/details/70238902 锁是计算机协调多个进程或纯线程并发访问某一资源的机制.在数据库中,除了传统的计算机 ...
- HDU2433—Travel (BFS,最短路)
Travel Time Limit: 10000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Sub ...
- (转译)2019年WEB漏洞扫描工具和软件前十名推荐
这些工具都有助于发现漏洞,从而最大限度地提高测试人员的时间和效率.这些工具,2019年更新,也可用于寻找漏洞. 为何扫描? 这资源是什么? Web应用程序对黑客具有极大的吸引力,并且出于百万种不同的原 ...
- Confluence备份,数据迁移
一.Confluence的备份.恢复1)Confluence的备份 管理员账号登录Confluence,点击右上角的"一般配置"-"每日备份管理",如下图(默认 ...
- Tomcat报错,内存溢出的错误Exception in thread "http-bio-8080-exec-13" java.lang.OutOfMemoryError: PermGen space
说是tomcat 内存溢出,然后按照网上看的搞一搞之后没出息这个问题了 原因是tomcat内存溢出,按照一到五步奏,在第三步加上那段参数.
- C++入门经典-例6.14-通过指针连接两个字符数组
1:字符数组是一个一维数组,引用字符数组的指针为字符指针,字符指针就是指向字符型内存空间的指针变量. char *p; char *string="www.mingri.book" ...
- C++入门经典-例3.16-使用do-while循环进行计算
1:代码如下: // 3.16.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> usin ...
- forms authentication原理
细说ASP.NET Forms身份认证 asp.net 登陆验证 Form表单验证的3种方式 Understanding and Implementing ASP.NET Custom Forms A ...