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的 ...
随机推荐
- 在qt的QOpenGLWidget开启opengl的抗锯齿
在QOpenGLWidget的构造函数添加下面几句代码即可 QSurfaceFormat surfaceFormat; surfaceFormat.setSamples();//多重采样 setFor ...
- 暑假集训#2 div1 J 四点直角 J - Space Invader 四点共面+跨立实验
题意:给你四个点,判断能否先依次通过A,B两点,然后再在某个地方只进行一次直角转弯再一次经过C,D两点: #include <iostream> #include <cstdio&g ...
- vue中axios的封装(注意这里面异步的概念和用法十分重要)
todo https://www.cnblogs.com/chaoyuehedy/p/9931146.html
- windows基础提权
Window基础提权 提到system权限 甚至让他变成你的肉鸡 我们了解一下windows下面有那些用户 Guests是用户最低的权限 而且一般是被禁用的 User权限也很低 连关机都不行 还有wi ...
- [CSP-S模拟测试]:硬币(博弈论+DP+拓展域并查集)
题目传送门(内部题135) 输入格式 第一行包含一个整数$T$,表示数据组数. 对于每组数据,第一行两个整数$h,w$,表示棋盘大小. 接下来$h$行,每行一个长度为$w$的字符串,每个位置由为$o, ...
- RedisTemplate集合使用说明-opsForList(二)
1.leftPush(K key, V value) 在变量左边添加元素值. Java代码 redisTemplate.opsForList().leftPush("list" ...
- ZooKeePer总汇
一.什么Zookeeper Zookeeper是一个分布式开源框架,提供了协调分布式应用的基本服务,它向外部应用暴露一组通用服务——分布式同步(Distributed Synchronization) ...
- 顺序表栈C语言实现
/* * SeqStack.h * * Created on: 2019年8月1日 * Author: Administrator */ #ifndef SEQSTACK_H_ #define SEQ ...
- 套接字之recvfrom系统调用
recvfrom系统调用通过用户传入的接收空间构造msghdr,并且调用sock_recvmsg,该函数调用socket操作的recvmsg函数sock->ops->recvmsg,ipv ...
- 【git】本地git bash连接远程库github
重要参考: https://www.liaoxuefeng.com/wiki/896043488029600 https://segmentfault.com/a/1190000003728094 正 ...