1025: [SCOI2009]游戏

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 2727  Solved: 1794
[Submit][Status][Discuss]

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,1 <= N <= 1000

Output

  包含一个整数,可能的排数。

Sample Input

【输入样例一】
3
【输入样例二】
10

Sample Output

【输出样例一】
3
【输出样例二】
16
 
这其实更像一道数学题。。。
以题目中N=6为例:
1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6
可以划分为(1,2,3) (4,5) (6) 三个循环节,模拟计算几组数据后发现都可以划分为这样的循环节
循环节的长度之和正好等于N,(即:3+2+1=6),而一个可能的排数等于LCM(循环节长度),即所有循环节长度的公倍数+1
因此问题转化为:和为N的一串数,求它们的最小公倍数,而这一串数可以继续分解成更小的数(即这一串数不是固定的),并继续求最小公倍数,所有可能的最小公倍数的总数,即为方案数
如:
一串数    最小公倍数
6        6
5 1       5
4 2       4
4 1 1       4
3 3       3
。。。。。。
最终可以发现可行的最小公倍数是:1,2,3,4,5,6,这六种,因此答案为6
而怎么求这一串数又成了问题,这里可以反过来思考,一个可行的最小公倍数需要满足的条件。
最小公倍数一定是一个合数,而一个合数可以分解为多个质数的积,那么最小公倍数的一个因数就是多个质数相乘后的积,并且需要满足所有因数的和小于等于N
因此可以得到最小公倍数Z=a1^b1 × a2^b2 × a3^b3 × ...(a为质数),如6=3^1 × 2^1
等于N我们都知道,至于为什么可以小于N,假设N=6为例,其中一种可行的最小公倍数,6=3^1×2^1,而3+2≤6。
因为作为这一串数:
一串数    最小公倍数
3 2 1       6
可以补1这种情况。
接下来就要用到动态规划,设f[i][j],i表示前i个质数,j表示因数的和,表示前i个质数,因数和小于等于N的情况总数
 
 
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 
 6 #define LL long long
 7 
 8 const int MAXN=10000;
 9 int cnt=0,prime[MAXN];
10 int n;
11 LL ans,f[200][1100];
12 void Prime(int x)
13 {
14     bool mark[MAXN];
15     for(int i=2;i<=x;i++)
16     {
17         if(!mark[i]) prime[++cnt]=i;
18         for(int j=1;j<=cnt&&prime[j]*i<=x;j++)
19         {
20             mark[prime[j]*i]=1;
21             if(i%prime[j]==0) break;
22         }
23     }
24 }
25 
26 int main()
27 {
28     scanf("%d",&n);
29     Prime(n);
30     f[0][0]=1;
31     for(int i=1;i<=cnt;i++)
32         for(int j=0;j<=n;j++)
33         {
34             f[i][j]+=f[i-1][j];
35             for(int k=prime[i];k<=j;k*=prime[i])
36                 f[i][j]+=f[i-1][j-k];
37         }
38     for(int i=0;i<=n;i++)
39         ans+=f[cnt][i];
40     cout<<ans<<endl;
41     return 0;
42 }
原文地址:https://www.cnblogs.com/InWILL/p/9265698.html