【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

Sample Output

【输出例子一】
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;
}



posted @ 2017-06-03 08:53  zhchoutai  阅读(108)  评论(0编辑  收藏