康托(Cantor)展开
直接进入正题。
康托展开
现在有"ABCDEFGHIJ”10个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的?
Input
第一行有一个整数n(0<n<=100000);
随后有n行,每行是一个排列;
Output
输出一个整数m,占一行,m表示排列是第几位;
ABCDEFGHIJ
HGEBFACDJI
GDEDHJBXIA
2803322
1911924
1 #include<bits/stdc++.h>
2 using namespace std;
3
4 int n,f[19];
5 int main(){
6 scanf("%d",&n);
7 f[0]=1;//注意
8 for(int i=1;i<=10;i++) f[i]=f[i-1]*i;//预处理阶乘
9 while(n--)//数据组数
10 {
11 int ans=1;//每个字符串排名都加1,上面有提到
12 string s;
13 cin>>s;
14 int len=s.length();//取字符串长度
15 for(int i=0;i<len;i++)
16 {
17 int k=0;
18 for(int j=i+1;j<len;j++)
19 if(s[i]>s[j]) k++;//记录在当前字符之后有几个比当前字符小的
20 ans+=k*f[len-i-1];//注意i从0开始,所以是len-i-1
21 }
22 printf("%d\n",ans);
23 }
24 return 0;
25 }
但是时间复杂度达到了len2(当然这里len只有11,除了穷举都能过)
但如果是这道题呢?(参考洛谷P5637)
P5367 【模板】康托展开
题目描述
求1∼N的一个给定全排列在所有1∼N全排列中的排名。结果对998244353取模。
输入格式
第一行一个正整数N。
第二行N个正整数,表示1∼N的一种全排列。
输出格式
一行一个非负整数,表示答案对9982443539取模的值。
输入输出样例
3
2 1 3
3
4
1 2 4 3
2
说明/提示
对于10%数据,1≤N≤10。
对于50%数据,1≤N≤5000。
对于100%数据,1≤N≤1000000
如果用传统的康托展开做,时间复杂度O(n2),只能拿50分。
我们考虑用数据结构维护每个数后面的它小的数的个数,自然而然想到了权值线段树(其实是我不会树状数组)。
线段树的叶子节点维护的是该叶子结点所对应的编号出现的次数(我也说不清楚,具体看代码)。首先插入所有数,随后第i次找1到a[i]-1区间内的和,做完之后删除这个数,防止后面的数重复记录产生错误答案。时间复杂度O(logn)。
上代码:
#include<bits/stdc++.h>//随时记得取模,尽可能降低WA的概率
#define P 998244353
#define N 1000009
using namespace std; int n,ans=,a[N],sum[N<<];
long long f[N];//这道题内存限制只有31.25MB,第一次全开了long long发现MLE,第二次全开了int爆WA,最后部分开了long long才压内存过
void Update(int rt,int l,int r,int x,int c,int fg)//fg是flag,标记
{
if(l==r && l==x)
{
if(fg==) sum[rt]+=c;//如果是插入,sum就加c
else sum[rt]-=c;//如果是删除,sum就减c
return;
}
int mid=(l+r)>>;
if(x<=mid) Update(rt<<,l,mid,x,c,fg);
else Update(rt<<|,mid+,r,x,c,fg);
sum[rt]=sum[rt<<]+sum[rt<<|];
} int Query(int rt,int l,int r,int x,int y)
{
if(l==x && r==y) return sum[rt];
int mid=(l+r)>>;
if(y<=mid) return Query(rt<<,l,mid,x,y);
else if(x>mid) return Query(rt<<|,mid+,r,x,y);
else return Query(rt<<,l,mid,x,mid)+Query(rt<<|,mid+,r,mid+,y);
}
int main(){
memset(sum,,sizeof(sum));
scanf("%d",&n);
f[]=;//注意
for(int i=;i<=;i++) f[i]=f[i-]%P*i%P;//计算阶乘
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
Update(,,n,a[i],,);//将每一个数插入到权值线段树中
}
for(int i=;i<=n;i++)
{
if(a[i]==)//如果a[i]==1,那么该数一定对答案没有贡献,直接删除
{
Update(,,n,a[i],,);
continue;
}
ans=(ans+Query(,,n,,a[i]-)%P*f[n-i]%P)%P;//更新答案
Update(,,n,a[i],,);//删除这个数
}
printf("%d\n",(ans+P)%P);
return ;
}
康托(Cantor)展开的更多相关文章
- poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)
题目来源: http://poj.org/problem?id=1077 题目大意: 给你一个由1到8和x组成的3*3矩阵,x每次可以上下左右四个方向交换.求一条路径,得到12345678x这样的矩阵 ...
- HDU 1027 Ignatius and the Princess II(康托逆展开)
Ignatius and the Princess II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K ( ...
- 康托展开&&康托逆展开
康托展开 简介:对于给定的一个排列,求它是第几个,比如54321是n=5时的第120个.(对于不是1~n的排列可以离散化理解) 做法: ans=a[n]*(n-1)!+a[n-1]*(n-2)!+~~ ...
- 康托展开&康托逆展开 的写法
康托展开 康托展开解决的是当前序列在全排序的名次的问题. 例如有五个数字组成的数列:1,2,3,4,5 那么1,2,3,4,5就是全排列的第0个[注意从0开始计数] 1,2,3,5,4就是第1个 1, ...
- CDOJ 485 UESTC 485 Game (八数码变形,映射,逆cantor展开)
题意:八数码,但是转移的方式是转动,一共十二种,有多组询问,初态唯一,终态不唯一. 题解:初态唯一,那么可以预处理出012345678的所有转移情况,然后将初态对012345678做一个映射,再枚举一 ...
- [知识点]Cantor展开
// 此博文为迁移而来,写于2015年3月14日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vtyo.html 1.含 ...
- 分形之康托(Cantor)三分集
1883年,德国数学家康托(G.Cantor)提出了如今广为人知的三分康托集,或称康托尔集.三分康托集是很容易构造的,然而,它却显示出许多最典型的分形特征.它是从单位区间出发,再由这个区间不断地去掉部 ...
- 【数学】康托展开 && 康托逆展开
(7.15)康托展开,就是把全排列转化为唯一对应自然数的算法.它可以建立1 - n的全排列与[1, n!]之间的自然数的双向映射. 1.康托展开: 尽管我并不清楚康托展开的原理何在,这个算法的过程还是 ...
- lightoj1060【康托逆展开】
可以先看些资料:http://blog.csdn.net/keyboarderqq/article/details/53388936 参考谷巨巨:http://blog.csdn.net/azx736 ...
- 康托展开+逆展开(Cantor expension)详解+优化
康托展开 引入 康托展开(Cantor expansion)用于将排列转换为字典序的索引(逆展开则相反) 百度百科 维基百科 方法 假设我们要求排列 5 2 4 1 3 的字典序索引 逐位处理: 第一 ...
随机推荐
- springboot+redis实现session共享
1.场景描述 因项目访问压力有点大,需要做负载均衡,但是登录使用的是公司统一提供的单点登录系统,需要做session共享,否则假如在A机器登录成功,在B机器上操作就会存在用户未登录情况. 2. 解决方 ...
- Java编程思想:进程控制
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public ...
- 洛谷P1140 相似基因
题目:https://www.luogu.org/problemnew/show/P1140 分析: 本题一看就知道是一道动归,其实和字串距离非常的像,只不过多了题目规定的匹配相似度罢了. 匹配的相似 ...
- [leetcode] 111.Mininum Depth of Binary Tree (Easy)
原题 寻找二叉树最短深度 这里用了dfs,beat 100%,4ms class Solution { public: int minDepth(TreeNode *root, int minNum ...
- 简单分析ThreadPoolExecutor回收工作线程的原理
最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了. 不过,我倒是对线程池是如何回收工 ...
- 《VR入门系列教程》之10---3D图形学初识
第三章 基于Oculus Rift开发桌面端VR应用 接下来的几个章节中我们会进行VR开发的实际操练,本章就从Oculus Rift开发开始,我们会介绍如何开发一个桌面端的VR应用.虽然只是介 ...
- Servlet和JSP知识总结
1.Servlet接口有哪些方法及Servlet生命周期 Servlet接口定义了5个方法,前三个方法与Servlet生命周期有关: void init() void service() void d ...
- 林大妈的JavaScript基础知识(三):JavaScript编程(1)对象
1. 对象的简单介绍与一些注意事项 JavaScript中具有几个简单数据类型:数字.字符串.布尔值.null值以及undefined值.除此之外其余所有值(包括数组.函数,甚至正则表达式)都是对象. ...
- 一文带你了解Java反射机制
想要获取更多文章可以访问我的博客 - 代码无止境. 上周上班的时候解决一个需求,需要将一批数据导出到Excel.本来公司的中间件组已经封装好了使用POI生成Excel的工具方法,但是无奈产品的需求里面 ...
- 序列化Serializable接口
一.序列化 1.什么是序列化? 序列化就是将对象的状态存储到特定存储介质中的过程,也就是将对象状态转换为可保持或传输格式的过程. 在序列化过程中,会将对象的公有成员.私有成员(包括类名),转换为字节流 ...