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

题目描述

今有正整数 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. 解决安装flask库不成功

    Python中使用python -m pip install --upgrade pip升级pip时老是不成功   场景 在使用python -m pip install --upgrade pip进 ...

  2. apache ignite系列(三):数据处理(数据加载,数据并置,数据查询)

    ​ 使用ignite的一个常见思路就是将现有的关系型数据库中的数据导入到ignite中,然后直接使用ignite中的数据,相当于将ignite作为一个缓存服务,当然ignite的功能远不止于此,下面以 ...

  3. FJUT2019暑假周赛一题解

    A.排队问题*-* 题意就是有长度为L的序列,每位的取值可以是'f'或者'm',问不包含'fff'和'fmf'的个数. 打表找规律 不难找出递推公式为F[n] = F[n-1] + F[n-3] + ...

  4. 使用Git工具批量拉取代码

    公司项目比较多,每天上班第一件事就是拉取代码,cd A 目录 git pull cd .. cd B ...... 一个项目一个项目的拉取,感觉也是很费劲的,那么有没有什么一键操作呢 现在执行一个命令 ...

  5. ascii codec can't decode byte 0xe8 in position 0:ordinal not in range(128)---python中文编码问题

    解决方案一:将如下部分加在报错的py文件里 import sys reload(sys) sys.setdefaultencoding('utf-8')

  6. 在vue中使用[provide/inject]实现页面reload

    在vue中实现页面刷新有不同的方法: 如:this.$router.go(0),location.reload()等,但是或多或少会存在问题,如页面会一闪等 所以建议使用[provide/inject ...

  7. Spring 梳理-容器(container)

    虽然Spring的组件代码是轻量级的,但它的配置却是重量级的.一开始,Spring用XML配置,而且是很多XML配置.Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显 ...

  8. java List转换和数组互转

    1.List转Array ArrayList<String> list=new ArrayList<String>(); String[] strings = new Stri ...

  9. Windows Terminal 安装及美化

    windows terminal 是今年微软Build大会上推出的一款的全新终端,用来代替cmder之类的第三方终端.具有亚克力透明.多标签.Unicode支持(中文,Emoji).自带等宽字体等这些 ...

  10. 我的mongoDb之旅(二)

    题序:上一回,咱们简单的操作了一些增减改查的操作,这一次,再来进行一场奇妙之旅 一.案例讲解 (1).字段有值与没值时的单条数据 第一条数据title这个字段(mysql用久了,习惯这么叫了)是没有数 ...