康托展开

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

 

康托展开

上一篇:AtCoder Beginner Contest 213【A - E】


下一篇:MarkDown学习 DOS命令