题目描述
题解
这题是BZOJ1025很像,但是这个要求换的次数最多,即lcm最大
那么可以dp
预处理出质数(组)了之后,f(i,j)表示选到第i组,和为j的最大乘积
然后记录一下方案,最后再递归回去
因为要字典序最小所以前面所有的1都不变,然后将选出了的质数(幂)从小到大排序,每一个循环节将第一个挪到后面去然后其余的向前推就行了
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 105
int T,n,ans,ansi,ansj;
int p[N],prime[N],bag[N][20],cnt[N],num[N],a[N];
struct data
{
int val,prei,prej,ch;
}f[N][N];
void get_p()
{
for (int i=2;i<=100;++i)
{
if (!p[i]) prime[++prime[0]]=i;
for (int j=1;j<=prime[0]&&i*prime[j]<=100;++j)
{
p[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
for (int i=1;i<=prime[0];++i)
{
bag[i][cnt[i]=1]=prime[i];
int now=prime[i];
while (now*prime[i]<=100)
{
now*=prime[i];
bag[i][++cnt[i]]=now;
}
}
}
void calc(int i,int j)
{
if (f[i][j].ch) num[++num[0]]=f[i][j].ch;
if (f[i][j].prei&&f[i][j].prej) calc(f[i][j].prei,f[i][j].prej);
}
int main()
{
get_p();
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
memset(f,0,sizeof(f));
for (int i=0;i<=n;++i) f[i][0].val=1;
for (int i=1;i<=prime[0];++i)
for (int j=0;j<=n;++j)
{
f[i][j].val=f[i-1][j].val;
f[i][j].prei=i-1,f[i][j].prej=j;
f[i][j].ch=0;
for (int k=1;k<=cnt[i];++k)
if (j>=bag[i][k])
{
if (f[i-1][j-bag[i][k]].val*bag[i][k]>f[i][j].val)
{
f[i][j].val=f[i-1][j-bag[i][k]].val*bag[i][k];
f[i][j].prei=i-1,f[i][j].prej=j-bag[i][k];
f[i][j].ch=bag[i][k];
}
}
else break;
}
ans=0;
for (int i=1;i<=prime[0];++i)
for (int j=0;j<=n;++j)
if (f[i][j].val>ans)
{
ans=f[i][j].val;
ansi=i,ansj=j;
}
num[0]=0;
calc(ansi,ansj);
sort(num+1,num+num[0]+1);
int sum=n;
for (int i=1;i<=num[0];++i) sum-=num[i];
for (int i=1;i<=n;++i) a[i]=i;
int now=sum+1;
for (int i=1;i<=num[0];++i)
{
for (int j=now;j<now+num[i]-1;++j)
a[j]=j+1;
a[now+num[i]-1]=now;
now+=num[i];
}
printf("%d ",ans);
for (int i=1;i<=n;++i) printf("%d%c",a[i]," \n"[i==n]);
}
}