考虑到本文读者年龄原因,本文改为使用简体中文撰写。

题目描述

今有正整数 n,kn,kn,k,求 1−n1-n1−n 共 nnn 个数的全排列,按字典序的第 kkk 个。

数据满足 1≤n≤105,1≤k≤min⁡(n!,1020 000).1\leq n\leq10^5,1\leq k\leq\min(n!,10^{20\ 000}).1≤n≤105,1≤k≤min(n!,1020 000).

Solution\text{Solution}Solution

请注意 kkk 的上限! ls忘了打。这很重要!

对于 50%50\%50% 及更低的数据,ls已经解释得很详尽,在此无需赘述。

尝试使用 DeCantor Expansion(逆康托展开)算法求解。这篇文章有详细解释,看不懂找 ls 他英语一级棒。时间复杂度 O(n2)O(n^2)O(n2);

预处理 1−n1-n1−n 的阶乘,时间复杂度 O(n2)O(n^2)O(n2);

朴素的高精度除法,时间复杂度 O(n2)O(n^2)O(n2);

综上所述,总的时间复杂度为 O(n2)O(n^2)O(n2)。贴上代码。

#include<cstdio>
#include<cstdlib>
#include<cstring> #define reg register
typedef long long ll; const int MAXN=1010; //最长 1000位
const int BYTE=8; //压8位,也就是说一个int代表8位数
const int MB=MAXN/BYTE+1; //新的长度 int pw[BYTE+10]; //pw[i]表示10^i
char str[MAXN+10]; struct Bignum{ //高精度类
int a[MB+10];
Bignum(){
memset(a,0,sizeof(a));
}
void read(){ //读入、赋值、压位
scanf("%s",str+1);
int len=strlen(str+1);
for(reg int i=1;i<=len;++i)
a[i]=str[len-i+1]-'0';
for(reg int i=1;i<=len/BYTE+1;++i){
int cnt=0;
for(reg int j=i*BYTE;j>=(i-1)*BYTE+1;--j)
cnt=cnt*10+a[j];
a[i]=cnt;
}
//注意清零
for(reg int i=len/BYTE+2;i<=len;++i) a[i]=0;
}
void print(){ //打印一个数
bool tf=0;
for(reg int i=MB+1;i>=1;--i){
if(a[i]&&!tf){
tf=1;
printf("%d",a[i]);
continue;
}
if(tf) printf("%08d",a[i]);
}
if(!tf) putchar('0');
}
//重定义Bignum类的运算,高精+高精
friend Bignum operator+(const Bignum a,const Bignum b){
Bignum c;
for(reg int i=1;i<=MB+1;++i)
c.a[i]=a.a[i]+b.a[i];
for(reg int i=1;i<=MB+1;++i)
if(c.a[i]>=pw[BYTE]){
c.a[i+1]+=c.a[i]/pw[BYTE];
c.a[i]%=pw[BYTE];
}
return c;
}
//高精减
friend Bignum operator-(const Bignum a,const Bignum b){
Bignum c;
for(reg int i=1;i<=MB+1;++i)
c.a[i]=a.a[i]-b.a[i];
for(reg int i=1;i<=MB+1;++i)
if(c.a[i]<0){
c.a[i]+=10;
--c.a[i+1];
}
return c;
}
//高精乘低精
friend Bignum operator*(const Bignum a,const int b){
Bignum c;
ll d[MB+10];
for(reg int i=1;i<=MB+1;++i)
d[i]=(ll)a.a[i]*b;
for(reg int i=1;i<=MB+1;++i){
if(d[i]>=pw[BYTE]){
d[i+1]+=d[i]/pw[BYTE];
d[i]%=pw[BYTE];
}
c.a[i]=(int)d[i];
}
return c;
}
//注意此处的>等价于>=
friend bool operator>(const Bignum a,const Bignum b){
for(reg int i=MB+1;i>=1;--i)
if(a.a[i]!=b.a[i])
return a.a[i]>b.a[i];
return 1;
}
}ft[MB+10],k,one;
//ft[i]表示i的阶乘
struct Pair{ //两个大数
Bignum a,b;
};
int n;
bool tf[MAXN+10]; //tf[i]表示第i个数是否可以被取用
int ans[MAXN+10]; //ans是答案数组 //朴素高精除,返回的a是商,b是余数
Pair operator/(const Bignum a,const Bignum b){
Pair c;Bignum d;
for(reg int i=MB+1;i>=1;--i){
d=d*10;d.a[1]=a.a[i];
c.a=c.a*10;
while(d>b){
d=d-b;
++c.a.a[1];
}
}
c.b=d;
return c;
}
void reset(){ //初始化
pw[0]=1;
for(reg int i=1;i<=BYTE;++i)
pw[i]=pw[i-1]*10;
ft[1].a[1]=1;
for(reg int i=2;i<=n;++i)
ft[i]=ft[i-1]*i;
memset(tf,1,sizeof(tf));
one.a[1]=1;
k=k-one;
}
int cg(const Bignum a){ //将一个Bignum转换成int
int cnt=0;
for(reg int i=MB+1;i>=1;--i)
cnt=cnt*pw[BYTE]+a.a[i];
return cnt;
}
int find(int x){ //找到能使用的第x小的数
int cnt=0;
for(reg int i=1;i<=n;++i)
if(tf[i]){
++cnt;
if(cnt==x){
tf[i]=0; //取用它
return i;
}
}
return -1;
}
void work(){ //DeCantor Expansion
Pair p;
int cnt;
//此处参考算法解释的文章
for(reg int i=1;i<n;++i){
p=k/ft[n-i];k=p.b;
cnt=cg(p.a);
ans[i]=find(cnt+1);
}
ans[n]=find(1); //最后只剩一个数了,取用它
}
int main(){
scanf("%d",&n);k.read();
reset();
work();
for(reg int i=1;i<=n;++i)
printf("%d ",ans[i]);
}

最后一个点的 100 000100\ 000100 000,看上去很吓人,但是由于 k≤1020 000k\leq10^{20\ 000}k≤1020 000,所以前 96 00096\ 00096 000 位左右都是原来的顺序(本质上还是 n2n^2n2 的)。这个点的特判我不管了你自己写。

优化方法

  1. 使用树状数组维护一个数是否被取用。时间复杂度 O(n log⁡ n)O(n\ \log\ n)O(n log n);
  2. 快速多项式除法,或者二分答案。时间复杂度 O(n log⁡ n)O(n\ \log\ n)O(n log n);
  3. 使用压位存储。若压 BYTEBYTEBYTE 位,则时间复杂度 O(O(原来)BYTE2)O(\frac{O(原来)}{BYTE^2})O(BYTE2O(原来)​);
  4. 使用任意模数快速阶乘。时间复杂度 O(n log⁡ n)O(n\ \log\ n)O(n log n)(未实践)。

期望时间复杂度 O(n log⁡ n)O(n\ \log\ n)O(n log n)。若哪位巨佬实现了,请评论并收下我的膝盖orz。

Did You AK Today? (今天你AK了吗?)的更多相关文章

  1. 德州扑克AK打法攻略

    AK是所有德扑网游中最受争议的底牌,也是一副令人又爱又恨的底牌.<德州扑克培训大师>根据国内德州扑克网游特性,为大家制作了第一套AK打法攻略,希望所有玩家从今天开始能正确认识AK,发挥AK ...

  2. 业务网关之AK中心建设

    啥是AK AK(Access Key)是一种身份证明,它解决了"资源的使用者是谁"这个问题,比如在生活中,身份证可以证明你是你,而在云计算或程序中,AK能证明你是这个应用的拥有者. ...

  3. 从云AK泄露利用看企业特权管理

    从云AK泄露利用看企业特权管理 目录 - 缘起 - 当前主流AK泄露检测方式 - 防止AK滥用的关键要素? - 哪些算特权账号管理? - 如何做特权账号管理? - 特权管理与堡垒机.IAM.零信任的关 ...

  4. //给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和

    //给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和 # include<stdio.h> void main() { ,sum1; ]={,- ...

  5. 百度地图秘钥ak的获取

    今天打开网站的时候出现了这个问题“百度未授权使用地图API, 可能是因为您提供的密钥不是有效的百度开放平台密钥或此密钥未对本应用的百度地图JavasoriptAPI授权.....”经过研究终于知道什么 ...

  6. 百度地图LBS开放平台AK一直没有用

    http://api.map.baidu.com/geoconv/v1/?coords=114.21892734521,29.575429778924;114.21892734521,29.57542 ...

  7. 移动平台作业——天气预报——天气数据的获得——为应用申请百度ak码

    需求: 可切换城市 可实时更新(按钮或手势或下拉刷新) 可现实未来三日的天气 不限制横屏或者竖屏,不限制布局样式,但要求得到的数据均需显示(北京.天气数据.天气图标) 提示: 获得实时天气数据(任选一 ...

  8. 点分治练习:不虚就是要AK

    [题面] 不虚就是要AK(czyak.c/.cpp/.pas) 2s 128M czy很火.因为又有人说他虚了.为了证明他不虚,他决定要在这次比赛AK. 现在他正在和别人玩一个游戏:在一棵树上随机取两 ...

  9. 设正整数n的十进制表示为n=ak……a1a0(0<=ai<=9,0<=i<=k,ak!=0),n的个位为起始数字的数字的正负交错之和T(n)=a0+a1+……+(-1)kak,证明:11|n的充分必要条件是11|T(n);(整除理论1.1.2))

    设正整数n的十进制表示为n=ak……a1a0(0<=ai<=9,0<=i<=k,ak!=0),n的个位为起始数字的数字的正负交错之和T(n)=a0+a1+……+(-1)kak, ...

  10. 有两个序列A和B,A=(a1,a2,...,ak),B=(b1,b2,...,bk),A和B都按升序排列。对于1<=i,j<=k,求k个最小的(ai+bj)。要求算法尽量高效。

    有两个序列A和B,A=(a1,a2,...,ak),B=(b1,b2,...,bk),A和B都按升序排列.对于1<=i,j<=k,求k个最小的(ai+bj).要求算法尽量高效. int * ...

随机推荐

  1. 并发编程之线程创建到销毁、常用API

    在前面一篇介绍了线程的生命周期[并发编程之多线程概念],在本篇将正式介绍如何创建.中断线程,以及线程是如何销毁的.最后,我们会讲解一些常见的线程API. 线程创建 Java 5 以前,实现线程有两种方 ...

  2. Python中使用pip安装库时提示:远程主机强迫关闭了一个现有的连接

    场景 在cmd中使用pip install moviepy时,需要安装一些依赖库,很长时间后提示: 远程主机中断了一个现有的连接. 原因是默认镜像源下载过慢,将其修改为国内或者设置安装时的源. 这里以 ...

  3. Android入门学习教程PDF免费下载

    场景 CSDN: https://blog.csdn.net/badao_liumang_qizhi 博客园: https://www.cnblogs.com/badaoliumangqizhi/ 哔 ...

  4. [LeetCode] 面试题之犄角旮旯 第叁章

    题库:LeetCode题库 - 中等难度 习题:网友收集 - zhizhiyu 此处应为一个简单的核心总结,以及练习笔记. 查找一个数“在不在”?桶排序理论上貌似不错. 回文问题 ----> [ ...

  5. PHP操作redis 【转】

    Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. Redis支持的数据类型有 Stirng(字符串), Lis ...

  6. Java基本数据类型转换及运算符

    上次我们说到完了Java中的基本数据类型,今天我们来说说Java中的基本数据类型转换和Java中的运算符 基本数据类型转换 java中可以从任意基本数据类型转型到外的基本数据类型 注意:(boolea ...

  7. selenium-05-常见问题

    一:日期控件 selenium不能直接对日期控件操作,可以通过js对日期控件做赋值操作 WebElement inputTimeBox=driver.findElement(by.name(" ...

  8. 如何快速转载CSDN中的博客

    看到一篇<如何快速转载CSDN中的博客>,介绍通过检查元素→复制html来实现快速转载博客的方法.不过,不知道是我没有领会其精神还是其他原因,测试结果为失败.

  9. 还在用SVN的人,要不要学Git?

    还在用SVN的人,要不要学Git? 提出这个问题,是因为很多小伙伴还不会使用Git. 在Git之前,是SVN的天下. SVN诞生于2001年,由于较为先进的管理方式而迅速取代了CVS. 很多80后小伙 ...

  10. SpringBoot集成Spring Security入门体验

    一.前言 Spring Security 和 Apache Shiro 都是安全框架,为Java应用程序提供身份认证和授权. 二者区别 Spring Security:重量级安全框架 Apache S ...