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; }