【bzoj1025】【SCOI2009】【游戏】【dp】
Description
windy学会了一种游戏。对于1到N这N个数字,都有唯一且不同的1到N的数字与之相应。最開始windy把数字按顺序1,2。3。……,N写一排在纸上。
然后再在这一排以下写上它们相应的数字。然后又在新的一排以下写上它们相应的数字。如此重复,直到序列再次变为1。2,3,……。N。
如: 1 2 3 4 5 6 相应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作例如以下 1 2 3 4 5 6 2 3 1 5 4 6 3 1 2 4 5 6 1 2 3 5 4 6 2 3 1 4 5 6 3 1 2 5 4 6 1 2 3 4 5 6 这时,我们就有若干排1到N的排列,上例中有7排。如今windy想知道,对于全部可能的相应关系。有多少种可能的排数。
Input
包括一个整数,N。
Output
包括一个整数。可能的排数。
Sample Input
【输入例子一】
3
【输入例子二】
10
3
【输入例子二】
10
Sample Output
【输出例子一】
3
【输出例子二】
16
3
【输出例子二】
16
HINT
【数据规模和约定】
100%的数据。满足 1 <= N <= 1000 。
题解:首先能够发现这个变换是由几个子集合的轮换构成的。然后最后答案就是这几个轮换长度的最小公倍数+1
然后轮换长度是从1-n的。那问题就变成了把n分成几个部分,然后这几个部分的最小公倍数的种类数。
最小公倍数肯定就是一坨素因子加加乘乘。
那么算出1-n有多少素因子。设f[i][j]表示用前i个素因子容量为j。
直接背包好了。。
#include<iostream> #include<cstdio> using namespace std; int n,t,p[1001]; long long ans,f[1001][1001]; bool ff[1001]; void get() { for(int i=2;i<=1000;i++) { if(!ff[i])p[++t]=i; for(int j=1;j<=t&&i*p[j]<=1000;j++){ff[i*p[j]]=true;if(i%p[j]==0 )break; } } } void dp() { f[0][0]=1; for(int i=1;i<=t;i++) { for(int j=0;j<=n;j++)f[i][j]=f[i-1][j]; for(int j=p[i];j<=n;j*=p[i]) for(int k=0;k<=n-j;k++) f[i][k+j]+=f[i-1][k]; } } int main() { cin>>n; get(); dp(); for(int i=0;i<=n;i++)ans+=f[t][i]; cout<<ans<<endl; }