题目链接:poj 3590 The shuffle Problem
题意:
给你一个数n,让你找一个字典序最小的置换序列,使得变换整个周期最大。
题解:
由于置换群的性质,我们可以将n拆分成m个数,使得这m个数的和为n,并且这m个数的最小公倍数最大。
dp可以求出将n拆分后的最大的最小公倍数。
然后可以将这个最大的最小公倍数分解为pi^mi+pi^mi+pi^mi...。
对于每一个pi^mi,就是一个循环的轮数。
然后将这些循环的轮数排序后输出对于的数字就行了。
PS:当前面的总和sum小于n时,前n-sum的循环轮数就全是1,这些数不会影响最大的最小公倍数。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define F(i,a,b) for(int i=a;i<=b;++i) 5 using namespace std; 6 typedef long long ll; 7 8 const int N=101; 9 10 int primes[N+1],tot=0; 11 bool vis[N+1]; 12 void Euler(){ 13 memset(vis,0,sizeof(vis)); 14 F(i,2,N){ 15 if(!vis[i])primes[++tot]=i; 16 F(j,1,tot){ 17 if(i*primes[j]>N)break; 18 vis[i*primes[j]]=1; 19 if(i%primes[j]==0)break; 20 } 21 } 22 } 23 24 int dp[N][N],mx[N],cnt,tmp,t,sum,n,ans[N]; 25 26 void init() 27 { 28 F(i,1,100)F(j,1,100)dp[i][1]=i; 29 F(i,1,100) 30 { 31 F(j,1,i) 32 { 33 F(k,1,i-1) 34 { 35 int tmp=dp[i-k][j-1]*k/__gcd(dp[i-k][j-1],k); 36 if(dp[i][j]<tmp)dp[i][j]=tmp; 37 } 38 } 39 F(j,1,i)mx[i]=max(dp[i][j],mx[i]); 40 } 41 } 42 43 int main() 44 { 45 Euler(),init(); 46 scanf("%d",&t); 47 while(t--) 48 { 49 scanf("%d",&n); 50 tmp=mx[n],sum=cnt=0; 51 printf("%d",mx[n]); 52 int tmp=mx[n],tp; 53 F(i,1,25) 54 { 55 if(primes[i]>tmp)break; 56 if(tmp%primes[i]==0) 57 { 58 tp=1; 59 while(tmp%primes[i]==0)tmp/=primes[i],tp*=primes[i]; 60 ans[++cnt]=tp,sum+=tp; 61 } 62 } 63 sort(ans+1,ans+1+cnt); 64 F(i,1,n-sum)printf(" %d",i); 65 int now=1,num=ans[now],ct=0; 66 F(i,n-sum+1,n) 67 { 68 if(ct==num-1) 69 printf(" %d",i-ct),ct=0,num=ans[++now]; 70 else printf(" %d",i+1),ct++; 71 } 72 puts(""); 73 } 74 return 0; 75 }