vijos - P1092全排列 (康托展开 + 康托展开的逆运算)

P1092全排列
Accepted
标签: [显示标签]

描述

输入两个自然数m,n 1<=n<=20,1<=m<=n!
输出n个数的第m种全排列。

如 :
输入 3 1
输出 1 2 3

格式

输入格式

在一行中输入n m

输出格式

一个数列,既n个数的第m种排列
每两个数之间空1格

样例1

样例输入1[复制]

3 2

样例输出1[复制]

1 3 2

限制

各个测试点1s

来源

lk


给予一个目标排列,求解他的全排列过程中的第几个排列是通过康托展开:
找出45231在这个排列中的顺序
比4小的数有3个
比5小的数有4个但4已经在之前出现过了所以是3个
比2小的数有1个
比3小的数有两个但2已经在之前出现过了所以是1个
比1小的数有0个
那么45231在这个排列中的顺序是3*4!+3*3!+1*2!+1*1!+0*0!+1=94
如此,可以发现,所谓的第几个则是少于某个数的个数乘以阶乘。


给予一个原序列,以及一个数n代表着目标排列是全排列中的第几个排列,是通过康托展开的逆运算:
一个数量为5的排列,现在要你找出第96种排序序列是什么
首先用96-1得到95(将他本身这一个种给删掉,可以直接判断)
用95去除4! 得到3余23
用23去除3! 得到3余5
用5去除2!得到2余1
用1去除1!得到1余0
有3个数比它小的数是4
所以第一位是4
有3个数比它小的数是4但4已经在之前出现过了所以是5(因为4在之前出现过了所以实际比5小的数是3个)
有2个数比它小的数是3
有1个数比它小的数是2
最后一个数只能是1
所以这个数是45321
具体情况,请查看代码


#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <iostream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <ctime>
#include <cctype>
using namespace std;


#define pb push_back
#define mp make_pair
#define fillchar(a, x) memset(a, x, sizeof(a))
#define copy(a, b) memcpy(a, b, sizeof(a))
#define S_queue<P> priority_queue<P, vector<P>,greater<P> >


typedef long long LL;
typedef pair<int, int > PII;
typedef unsigned long long uLL;
template<typename T>
void print(T* p, T* q, string Gap = " "){int d = p < q ? 1 : -1;while(p != q){cout << *p;p += d;if(p != q) cout << Gap; }cout << endl;}
template<typename T>
void print(const T &a, string bes = "") {int len = bes.length();if(len >= 2)cout << bes[0] << a << bes[1] << endl;else cout << a << endl;}

const int INF = 0x3f3f3f3f;
const int MAXM = 1e5;
const int MAXN = 1e4;
uLL F[21]={1,1,2,6,24,120,720,5040,40320,
           362880,3628800,39916800,
           479001600,6227020800,87178291200,
           1307674368000,20922789888000,355687428096000,
           6402373705728000,121645100408832000,2432902008176640000};
bool vis[30];
uLL Fig[25];
int main(){
    uLL n, m, cnt;
    cin >> n >> m;
    m --;
    for(uLL i = n - 1;i > 0;i --){
        uLL k = m / F[i];
        m %= F[i];
        cnt = k + 1;
        uLL xs = 1,ft = 0;
        while(xs <= cnt){
            ft ++;
            if(!vis[ft]) xs ++;
        }
        vis[ft] = true;
        cout << ft << " ";
    }
    for(int i = 1;i <= n;i ++){
        if(!vis[i]) cout << i;
    }
    cout << endl;
    return 0;
}


你可能感兴趣的:(vijos - P1092全排列 (康托展开 + 康托展开的逆运算))