·康托展开:
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 }