3232. 【佛山市选2013】排列

Description

一个关于n个元素的排列是指一个从{1, 2, …, n}到{1, 2, …, n}的一一映射的函数。这个排列p的秩是指最小的k,使得对于所有的i = 1, 2, …, n,都有p(p(…p(i)…)) = i(其中,p一共出现了k次)。

例如,对于一个三个元素的排列p(1) = 3, p(2) = 2, p(3) = 1,它的秩是2,因为p(p(1)) = 1, p(p(2)) = 2, p(p(3)) = 3。

给定一个n,我们希望从n!个排列中,找出一个拥有最大秩的排列。例如,对于n=5,它能达到最大秩为6,这个排列是p(1) = 4, p(2) = 5, p(3) = 2, p(4) = 1, p(5) = 3。

当我们有多个排列能得到这个最大的秩的时候,我们希望你求出字典序最小的那个排列。对于n个元素的排列,排列p的字典序比排列r小的意思是:存在一个整数i,使得对于所有j < i,都有p(j) = r(j),同时p(i) < r(i)。对于5来说,秩最大而且字典序最小的排列为:p(1) = 2, p(2) = 1, p(3) = 4, p(4) = 5, p(5) = 3。

Input

输入的第一行是一个整数T(T <= 10),代表数据的个数。

每个数据只有一行,为一个整数N。

Output

对于每个N,输出秩最大且字典序最小的那个排列。即输出p(1), p(2),…,p(n)的值,用空格分隔。

Sample Input

2

5

14

Sample Output

2 1 4 5 3

2 3 1 5 6 7 4 9 10 11 12 13 14 8

Data Constraint

对于40%的数据,有1≤N≤100。

对于所有的数据,有1≤N≤10000。

Solution

Ps:这题与oj的1038极度类似,但是1038简单些。大家若打完一题觉得很艰难,可以再打另一题熟练一下。

言归正传。

题目大意:题目说得很清楚,不再重复。

第一步:直观想法。

显然,对于一种排列,相当于给出了数字1~n的对应关系,且不重复不遗漏,刚好把1到n又包含了一遍。

对,连边!

每个数向它对应的数连边,这样我们就得到了一幅图,且这幅图很有特点——全是简单环。因为每个数的对应有且仅有一个,且不重复不遗漏,所以不存在大环套小环的情况,不存在复杂图形,而且,由于连了边,每个点是什么数字已经没有意义了。

第二步:问题转化

我们再观察这时候的答案,显然,对于一个排列,最大的秩就是所有环大小的最小公倍数。因此,问题转化成:

把n拆成几个正整数的和(正整数可以为1),这些正整数的最小公倍数就是我们要的最大的秩,而字典序最小的拆法就是我们要输出的东西。

至此,解法1出来了:暴力拆n,然后求最小公倍数,记录最大值和最大值的拆法。至于时间复杂度,你想想,暴力拆n就相当于全排列了,当然很慢了。

第三步:新的想法

既然我们已经知道答案就是某个最小公倍数,那我们为什么不直接构造这个最小公倍数?

最小公倍数归根到底是很多质数的乘积,因此我们直接用质数来构造。

假设现在有三个质数p1、p2、p3,它们的和<=n,那么p1*p2*p3一定是一个合法的答案。我们可以先弄一个大小为p1的环,然后弄一个大小为p2的环,再弄大小为p3的环,如果n还有剩余,那么剩下的通通自环,这样一定是合法的。

推广:现在把三个质数变成p1^c1、p2^c2、p3^c3,他们乘起来依然是合法的。跟上面其实是一样的道理。

再推广:设环的大小为w,那么w可不可以包含两种或以上素数?可以,但是完全可以转化成上面的情况处理。设w=p1^c1 * p2^c2 * p3^c3,它对最小公倍数的贡献就是w,但是如果我把它拆成三个环来处理,使得每个环大小仅包含一种质数,效果是一样的。而且拆了之后,环的大小也变小了,显然答案的排列会更优。

第四步:问题再次转化

现在问题变成:

现在有一堆质数(n以内),每个质数的使用次数上限是已知的(就是上面的c的大小上限)。我要选择一些质数(或它的幂),使他们的和<=n,然后这些质数(或它的幂)的乘积要最大。

而这其实就是有限背包问题。

设f[I,j]表示我们处理到第i个质数、当前和为j所能获得的最大秩。则f[I,j]=max(f[i-1,j-w]*w)。(这里的w是简写)。而我们要求排列的话,只需记录一下每个状态是由哪个状态转移过来的,最后还原即可。

至此,问题解决。

第五步:注意事项

f的值很大很大,大过long long,所以我们要把f的值转为自然对数来做。由于自然对数是单调函数,所以比较大小的方式一毛一样。

这里也可以用double解决。

Ps2:题解的实质在第四步。

Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define f(p) f[p][j]
#define g1(p) g[p][j][1]
#define g0(p) g[p][j][0]
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define N 10001
using namespace std;
double f[1230][N];
I T,n,x,y,xx,yy,a[N],p[N],c[N],bz[N],g[1230][N][2],ans[N],sum;
I main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	F(i,2,N-1) if(!bz[i]){
		p[++p[0]]=i;
		x=N-1;
		while(x/i){x/=i;c[p[0]]++;}
		F(j,i,(N-1)/i) bz[i*j]=1;
	}
	f[0][0]=1;
	F(i,1,p[0]){
		F(j,0,N-1) if(f(i-1)>f(i)){f(i)=f(i-1),g0(i)=g0(i-1),g1(i)=g1(i-1);}
		x=1;
		F(k,1,c[i]){
			x*=p[i];
			Fd(j,N-1,x) if(f[i-1][j-x]*x>f(i)) f(i)=f[g0(i)=i-1][g1(i)=j-x]*x;
			F(j,x+1,N-1) if(f[i][j-1]>f(i)) f(i)=f[g0(i)=i][g1(i)=j-1];
		}
	}
	I i,j;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		sum=a[0]=0;
		i=p[0],j=n;
		while(i){
			if(f(i)>f[g0(i)][g1(i)]) sum+=(a[++a[0]]=j-g1(i));
			xx=g0(i),yy=g1(i);i=xx,j=yy;
		}
		sort(a+1,a+1+a[0]);
		x=1;
		if(sum<n){
			x=n-sum+1;
			F(i,1,x) ans[i]=i;
		}
		F(i,1,a[0]){
			F(j,1,a[i]){ans[x]=x+1;x++;}
			ans[x-1]=x-a[i];
		}
		F(i,1,n) printf("%d ",ans[i]);
		printf("\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/107870971