【题意】略,中文题可自己查看!
【分析】在做题之前介绍一下置换群的基本概念。。。置换的概念是什么?一个有限集合的一一变换叫做置换,一对对置换组成了置换群。对于一个集合a(a[1],a[2],a[3]...a[n]) 通过置换可以变成 (b[a[1]],b[a[2]],b[a[3]]...b[a[n]]) b的作用就是置换(可以理解为某种函数的作用),将原来的集合映射成具有相应次序的集合a',a'可以看做是a的相同元素集合,不同的排列组合的一个集合。 每个n元的置换都可以表示成若干个互不相交的循环置换的乘积,设每个子循环置换的循环节为ci,则总的置换的循环节显然为lcm(c1,c2..cn)。
好了,那么这个题一眼就可以看出是典型的置换群了。顾而
排数=lcm{Ai,Ai表示循环节长度},∑i=1kAi=n
因此这个问题转化为了,把一个数n拆分成若干个数,问这若干个数的最小公倍数的可能情况数!计数问题,典型的dp转移!
【状态表示】
筛素数表,dp[i][j]表示j拆分的若干个数分解质因数之后最大质因数不超过第i个质因数的最小公倍数可能性。
dp[i][j]=dp[i-1][j]+sigma{dp[i][j-p[i]^k]}
[AC代码]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
bool used[1005];
int prime[1005];
int num,n;
ll dp[1005][1005];//i个质数和为j的总方案数
void make_tab(int x)
{
memset(used,false,sizeof(used));
for(int i=2; i<=x; i++)
{
if(!used[i])
{
prime[++num]=i;
for(int j=i*i; j<=x; j+=i)
{
used[j] = true;
}
}
}
}
void solve()
{
dp[0][0]=1;
for(int i=1; i<=num; i++)
{
for(int j=0; j<=n; j++)
{
dp[i][j] = dp[i-1][j];
}
for(int j=prime[i]; j<=n; j*=prime[i])
{
for(int k=0; k<=n-j; k++)
{
dp[i][j+k]+=dp[i-1][k];
}
}
}
ll ans = 0;
for(int i=0; i<=n; i++)
{
ans+=dp[num][i];
}
cout<<ans<<endl;
}
int main()
{
cin>>n;
make_tab(n);
solve();
return 0;
}
因此