[ZJOJ] 5772【NOIP2008模拟】今天你AK了吗?

Description

AK:All kill
“你为什么没背书?”
“没有为什么,我就是没背书。”
“……我去年买了个表,G—U—N!”
头铁王InFleaKing把背书的时间都拿去列排列了……
n=3的排列一共有六个(顺序按字典序从小到大):
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
气不打一处来的InFleaKing把n的排列打乱了。
他想知道字典序第k小的n的排列是什么?
由于InFleaKing被捉去背书了,所以这个问题只能交给被万人顶礼膜拜的dalao您来解决了。

Input

一行,两个数,分别是n,k。
n,k的含义见题目描述。

Output

一行,n个数。
代表字典序第k小的n的排列。
注意两两数之间要用空格隔开。

Sample Input

Sample Input1:

1 1

Sample Input2:

2 2

Sample Output

Sample Output1:

1

Sample Output2:

2 1

Data Constraint

【数据约定】
对于10%的数据:1<=n<=3
对于20%的数据,1<=n<=9
对于30%的数据:1<=n<=18,1<=k<=10^6
对于60%的数据:1<=n<=18
对于80%的数据:1<=n<=100,1<=k<=10^100
对于90%的数据:1<=n<=1000,1<=k<=10^1000
对于100%的数据:1<=n<=100000,1<=k<=min(10^20000,n!)

思路解析

无力吐槽。
如果想恶心我的话成功了。

根据惯例,我们面向数据编程。

第一档

对于10%的数据:1<=n<=3
手算 + 打表
预期分数:10

第二档

对于30%的数据:1<=n<=18,1<=k<=10^6
手算 + 打表还是可以
直接暴力递归求解。
预期分数:20 ~ 30

第三档

对于60%的数据:1<=n<=18
要不要听一个看上去很高级的玩意叫康托展开?
这个题听说是逆康托展开。没兴趣讲,请自行baidu

说一下我的想法
因为是输出字典序,我姑且列出所有情况看一下吧
以4 6这组数据为例

1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
……..

可以找出一个规律:
第一位数是当前1~4中没用过的第k/6大的数
第二位是当前1~4中没用过的第k/2大的数

6 = 3!
2 = 2!
好了,写代码吧

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;

const long long sum[25] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,6227020800,87178291200,1307674368000,20922789888000,355687428096000,6402373705728000};

long long n,k;
bool vis[25];

inline void find_kth(long long x) {
    long long res = 0;
    long long i;
    for(i = 1;i <= n;i++) {
        if(!vis[i]) res++;
        if(res == x) break;
    }
    printf("%lld ",i);
    vis[i] = 1;
}

int main() {
    freopen("array.in","r",stdin);
    freopen("array.out","w",stdout);
    scanf("%lld%lld",&n,&k);
    long long now,tmp,cnt,x;
    now = k;
    cnt = n;
    while(cnt--) {
        tmp = now / sum[cnt];
        x = now;
        now = now % sum[cnt];
        if(!now) now = sum[cnt];
        if(tmp * sum[cnt] < x) tmp++;
        find_kth(tmp);
    }
    return 0;
}
第四档

对于90%的数据:1<=n<=1000,1<=k<=10^1000
高精不解释
每次的商是小于n的,所以可以二分商涉及到高精度减法,高精度乘法,还算好打。
一次的复杂度是O(n log n)
总复杂度O(n^2 log n)
预期分数:70~90

第五档

对于100%的数据:1<=n<=100000,1<=k<=min(10^20000,n!)
以下引自作者的题解》》

k<=10^20000
我可以很负责任的告诉你:6100!>10^20000,但是我们的n却有10^5那么大。
这说明什么?
这说明前100000-6100=93900个数一定是1、2、3、…、93900(你发现了
吗?)
因此只需要O(6100^2)的复杂度来完成这个部分。
即使你不会数据结构或者分块也可以解决这道题!

假数据范围
预计分数:90~100(需要常数优化)

总结

考场不要写高精,除非你时间充裕

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值