·康托展开: 1.先算 1~n 的阶乘 jc[1]~jc[n] 2.计算每一位后面有几个比这一位小的数 , 记为 num[i] 3.公式: ans+=num[i]*jc[n-i] ( i : 1~n ) < ※ans+1※ 即为 该展开式为全排列中的第几个 > ·康托逆展开: 1.先算 1~n 的阶乘 jc[1]~jc[n] 2.※ m-- ※ ( 因为算康托展开时最后要加1,所以先减1 ) 3.把m不断除 jc[n-i] 得商和余数 , 商就是这位后面比他小的数,再刨去前面已用过的,就是这个数 e.g.: 1 3 4 5 2 为5的全排列 中的第10种 10-1=9 ; 9/(4!)=0……9; -> 第一位后面没有比他小的数 ->1 9/(3!)=1……3; -> 第二位后面有一个比他小的数 , 1用过 ->3 3/(2!)=1……1; -> 第三位后面有一个比他小的数 , 1,3用过 ->4 3/(2!)=1……1; -> 第四位后面有一个比他小的数 , 1,3,4用过 ->5 3/(2!)=1……1; -> 第五位后面没有比他小的数 , 1用过 ->2
1 #include<algorithm> 2 #include<iostream> 3 #include<cstdio> 4 using namespace std; 5 long long n,m,jc[101]; 6 bool vis[101]; 7 int main() 8 { 9 scanf("%lld%lld",&n,&m); 10 m--; //排列从1开始,从0开始没这事 11 jc[0]=1; 12 jc[1]=1; 13 for(long long i=2;i<=n;++i) 14 jc[i]=jc[i-1]*i; 15 for(long long i=1;i<=n;++i) 16 { 17 long long li=1,lj; 18 lj=m/jc[n-i]; 19 m-=lj*jc[n-i]; 20 while(vis[li]==true) 21 li++; 22 while(lj>0) 23 { 24 if(vis[li+1]==false) 25 lj--; 26 li++; 27 } 28 vis[li]=true; 29 printf("%lld ",li); 30 } 31 printf("\n"); 32 return 0; 33 }
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 long long n,ans,a[101],num[101],jc[101]; 5 int main() 6 { 7 scanf("%lld",&n); 8 jc[1]=1; 9 for(int i=2;i<=n;++i) 10 jc[i]=jc[i-1]*i; 11 for(int i=1;i<=n;++i) 12 scanf("%lld",&a[i]); 13 for(int i=1;i<=n;++i) 14 { 15 for(int j=i+1;j<=n;++j) 16 if(a[j]<a[i]) 17 num[i]++; 18 ans+=num[i]*jc[n-i]; 19 } 20 printf("%lld\n",ans+1); 21 return 0; 22 }