• [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(需要常数优化)

    总结

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

     
     
  • 相关阅读:
    邮件的DNS设置
    使用GIT SUBTREE集成项目到子目录(转)
    PostGIS 快速入门(转)
    PostgreSQL数据库的安装与PostGIS的安装(转)
    python sort、sorted高级排序技巧(转)
    jQuery选择器--简洁又全面(转)
    jQuery选择器(转)
    手机/电脑的定位方式
    iOS定位原理和使用建议(转)
    【震惊】一个白帽子居然在网咖做出这种事
  • 原文地址:https://www.cnblogs.com/floatiy/p/9458422.html
Copyright © 2020-2023  润新知