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

题目描述

今有正整数 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. myeclipse 保存含中文的jsp失败,提示内容含有 ISO-8859-1 不支持的字符

    就是这货,网上说各种设置首选项编码神马的,但我只是临时学一学jsp,装的myeclipse貌似不全,没有他们说的选项,后来发现了解决方案: 出错是因为我的jsp文件是用于在其他jsp中引入的,所以没有 ...

  2. linux 操作系统级别监控 nmon命令

    nmon是IBM公司开发的Linux性能监控工具,可以实时展示系统性能情况,也可以将监控数据写入文件中,并使用nmon分析器做数据展示 实时监控 命令 ./nmon c 代表CPU m 代表Memor ...

  3. python接口自动化测试二十七:密码MD5加密 ''' MD5加密 ''' # 由于MD5模块在python3中被移除 # 在python3中使用hashlib模块进行md5操作 import hashlib # 待加密信息 str = 'asdas89799,.//plrmf' # 创建md5对象 hl = hashlib.md5() # Tips # 此处必须声明encode # 若写法为

    python接口自动化测试二十七:密码MD5加密   ''' MD5加密 '''# 由于MD5模块在python3中被移除# 在python3中使用hashlib模块进行md5操作import has ...

  4. C# HTTP网络常用方法封装

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Regi ...

  5. Django-官网查询部分翻译(1.11版本文档)-QuerySet-字段查找-06

    目录 Making queries 进行查询 创建一个对象(一条数据记录) 保存修改的表对象 保存外键字段或多对多字段(ForeignKey or ManyToManyField fields) Re ...

  6. SQL手工注入进阶篇

    0.前言 上一篇我们介绍了SQL手工注入的流程以及步骤,但在实际的安全问题以及CTF题目中,查询语句多种多样,而且是肯定会对用户的输入进行一个安全过滤的,而这些过滤并不一定是百分百安全的,如何利用一些 ...

  7. Linux配置部署_新手向(四)——Redis安装与配置

    前言 配置完mysql之后,我们来紧接着安装redis,毕竟这些不用太多的思考,就是命令执行,配置文件,连接测试. 安装 首先,我们要看安装哪个版本,可以在Redis官网看看我们安装哪个版本. 在之前 ...

  8. Java 集合转换(数组、List、Set、Map相互转换)

    package com.example.test; import java.util.ArrayList; import java.util.Arrays; import java.util.Hash ...

  9. JQuery .width()/.css("width")方法 比较

    1. 获取到的值的区别 获取到的为实际宽度,不包括 内边距 和 边框: <div id="aa"> ...... </div> // 1. width() ...

  10. MongoDB的基本操作(增删改查)

    ​ 目录 概念整理 数据库:一个MongoDB中可以建立多个数据库. 集合:MongoDB的文档组. 文档:实际存放数据的地方. 常见的操作 数据库(新增,删除) 集合(新增,编辑,删除) 文档(增删 ...