题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4345
题目大意:求长度为n的数列的置换的循环节的长度的种数。
题目思路:以下摘自题解:
循环节的长度为各独立置换环长度的最小公倍数。问题即求相加和为N的正整数的最小公倍数的可能数。 由于1不影响最小公倍数,问题转化为相加小于等于N的若干正整数的最小公倍数的可能数。 如果这些正整数包含大于一个质因子,只会使得正整数的和更大。 因而问题再次转化为相加小于等于N的若干质数的最小公倍数的可能数。 N<1000,于是可递推得。
题解上说的若干质数的最小公倍数其实是若干长度为质数的幂的最小公倍数。这样可以用dp[i][j]表示取到前i个质数,长度小于等于j的方法数。具体见代码。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<string> #include<queue> #include<algorithm> #include<vector> #include<stack> #include<list> #include<iostream> #include<map> using namespace std; #define inf 0x3f3f3f3f #define Max 110 int max(int a,int b) { return a>b?a:b; } int min(int a,int b) { return a<b?a:b; } int flag[1010]; int prime[1010]; int cnt; __int64 dp[200][1010]; __int64 sum[1010]; void init() { cnt=1; int i,j; memset(flag,0,sizeof(flag)); for(i=2;i<=1000;i++) { if(flag[i]) continue; prime[cnt++]=i; for(j=i*i;j<=1000;j+=i) flag[j]=1; } } void solve() { int i,j,k; memset(dp,0,sizeof(dp)); for(i=0;i<=1000;i++) dp[0][i]=1; for(i=1;i<cnt;i++) for(j=0;j<=1000;j++) { dp[i][j]=dp[i-1][j]; for(k=prime[i];k<=j;k*=prime[i]) dp[i][j]+=dp[i-1][j-k]; } } int main() { init(); solve(); int n; while(scanf("%d",&n)!=EOF) { printf("%I64d\n",dp[cnt-1][n]); } }