【数学】全排列

描述 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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值