描述 Description | |||
输入两个自然数m,n 1<=n<=20,1<=m<=n! 输出n个数的第m种全排列。 如 : 输入 3 1 输出 1 2 3 | |||
输入格式 Input Format | |||
在一行中输入n m | |||
输出格式 Output Format | |||
一个数列,既n个数的第m种排列 每两个数之间空1格 | |||
样例输入 Sample Input | |||
样例输出 Sample Output | |||
时间限制 Time Limitation | |||
各个测试点1s |
尼玛LCY跟我说要用高精,我果断相信了,结果傻逼了半天。
对于第1位,因为第2位到第n位的排列数为(n-1)!,所以第1位就等于ceil(m/(n-1)!)。
对于第2位,不考虑第1位了,所以要找第2位到第n位的排列中的第(m%(n-1)!)大(设它为m2),因为第3位到第n位的排列数位(n-2)!,所以第2位就等于除开第1位的数中第ceil(m2/(n-2)!)大。
依此类推。
一开始以为是约瑟夫公式,后来想了下,不对,因为每次取的个数不同。
考虑了一下,好像是康拓展开的逆运算,果然是。。。= =、
囧啊。
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
using std::ceil;
typedef long long ll;
long n;
ll m;
ll f1[25];
ll jie[] = {1ll,1ll,2ll,6ll,24ll,120ll,720ll,5040ll,40320ll,362880ll,3628800ll,39916800ll,479001600ll,6227020800ll,87178291200ll,1307674368000ll,20922789888000ll,355687428096000ll,6402373705728000ll,121645100408832000ll,2432902008176640000ll};
long getint()
{
long rs=0;bool sgn=1;char tmp;
do tmp = getchar();
while (!isdigit(tmp)&&tmp-'-');
if (tmp == '-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}
ll getll()
{
ll rs=0;bool sgn=1;char tmp;
do tmp = getchar();
while (!isdigit(tmp)&&tmp-'-');
if (tmp == '-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}
long que[25] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24};
int main()
{
freopen("full_array.in","r",stdin);
freopen("full_array.out","w",stdout);
n = getint();
m = getll();
for (long i=1;i<n+1;i++)
{
f1[i] = long(ceil(double(m)/double(jie[n-i])));
m %= jie[n-i];
}
long total = n;
for (long i=1;i<n+1;i++)
{
long ths;
if (f1[i]==0) ths = total;
else ths = (f1[i]-1)%total+1;
printf("%ld ",que[ths]);
for (long j=ths;j<total;j++)
que[j] = que[j+1];
total --;
}
return 0;
}