hdu多校第五场1005 (hdu6628) permutation 1 排列/康托展开/暴力

题意:

定义一个排列的差分为后一项减前一项之差构成的数列,求对于n个数的排列,差分的字典序第k小的那个,n<=20,k<=1e4。

题解:

暴力打表找一遍规律,会发现,对于n个数的排列,如果想找到差分的字典序第k小的,如果k<=(n-1)!,那么对应的那个排列就是把第一位赋值为n,后面的是1~n-1的元素本身排列字典序第k小的。

比如,4个元素的排列的差分字典序最小的前6个分别是

4,1,2,3

4,1,3,2

4,2,1,3

4,2,3,1

4,3,1,2

4,3,2,1

当n为10或更多的时候,(n-1)!>1e4,便可用康托逆展开直接计算。

当n为9以下时,原先的想法是在本地暴力排序,对于每个n都取前1e4个元素交表。后来发现hdu限制提交代码大小,分析了一下,9!约等于3e5,暴力打表,过了。

#include<iostream>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std; 
int MAXN;
LL fact[25];
int tmp[30];
struct Node{
    int px[12];//排列
    int cha[12];//排列的差分
    int indexpx;
    int indexcha;
    friend bool operator > (const Node &a,const Node &b){
        for(int i=1;i<MAXN;i++){
            if(a.cha[i]!=b.cha[i])return a.cha[i]>b.cha[i];
        }
    }
    friend bool operator < (const Node &a,const Node &b){
        for(int i=1;i<MAXN;i++){
            if(a.cha[i]!=b.cha[i])return a.cha[i]<b.cha[i];
        }
    }
    //比较差分的字典序
}node[10000000];
int ans[10][100005];
void make_ans(){
    for(int i=1;i<=MAXN;i++){
        node[1].px[i]=i;
        node[1].cha[i-1]=1;
    }
    node[1].indexpx=1;
    int i,n;
    for(i=2;;i++){
        for(int j=1;j<=MAXN;j++){
            node[i].px[j]=node[i-1].px[j];
        }
        if(!next_permutation(&node[i].px[1],&node[i].px[MAXN+1])){
            n=i-1;
            break;
        }
        
        for(int j=1;j<MAXN;j++){
            node[i].cha[j]=node[i].px[j+1]-node[i].px[j];
        }
        
        node[i].indexpx=i;
    }
    sort(node+1,node+1+n);
    for(int i=1;i<=min(n,10000);i++){
        ans[MAXN][i]=node[i].indexpx;
    }
    //暴力打表预处理
}
void make_fact(){
    fact[0]=1;
    for(int i=1;i<=20;i++){
        fact[i]=fact[i-1]*i;
    }
}
void Cantor_invexp(int *p,int len,LL rank){
    //康托逆展开
    int temp[len];
    for(int i=0;i<len;i++){
        temp[i]=i+1;
    }
    for(int i=1;i<=len;i++){
        int a=rank/fact[len-i];
        rank%=fact[len-i];
        for(int j=0;j<len;j++){
            if(a==0 && temp[j]>0){
                p[i]=temp[j];
                temp[j]=0;
                break;
            }else if(temp[j]>0){
                a--;
            }
        }
    }
    return ;
}

int main(){
    make_fact();
    for(int i=2;i<=9;i++){
        MAXN=i;
        make_ans();
    }
    
//    return 0;
    int t;
    scanf("%d",&t);
    while(t--){
        int n,k;
        scanf("%d %d",&n,&k);
        if(n>=10){
            printf("%d",n);
            Cantor_invexp(tmp,n-1,1LL*k-1);
            for(int i=1;i<=n-1;i++){
                printf(" %d",tmp[i]);
            }
            printf("\n");
        }else{
            Cantor_invexp(tmp,n,1LL*ans[n][k]-1);
            for(int i=1;i<=n;i++){
                printf("%d",tmp[i]);
                if(i<n)printf(" ");
                else printf("\n");
            }
        }
    }
    return 0;
}
posted @ 2019-08-05 22:20  Isakovsky  阅读(429)  评论(1编辑  收藏  举报