【bzoj1025】[SCOI2009]游戏

xiaoxiao2021-02-28  101

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的数的集合的最小公倍数的可能个数。 对于一个答案可能的最小公倍数ans,ans=p1^a1*p2^a2*……*pk^ak。 如果ans符合,则需满足p1^a1+p2^a2+……+pk^ak<=n。因为如果符合此条件,我们令b1=p1^a1,b2=p2^a2……,其余的数用1来补就好了。然后DP就可以了。

代码

#include<cstdio> #include<cstring> #include<iostream> #define ll long long bool flag[1005]; int p[1005],tot,n; ll f[1005][1005],ans; using namespace std; void get_p() { for (int i=2;i<=n;i++) { if (!flag[i]) { p[++tot]=i; for (int j=i;j<=n;j+=i) flag[j]=1; } } } void solve() { f[0][0]=1;//f[i][j]表示用了前i个质数,和为j的方案数。 for (int i=1;i<=tot;i++) { for (int j=0;j<=n;j++) { f[i][j]=f[i-1][j]; int mi=p[i]; while (j-mi>=0) { f[i][j]+=f[i-1][j-mi]; mi*=p[i]; } } } for (int i=0;i<=n;i++) ans+=f[tot][i]; printf("%lld",ans); } int main() { cin>>n; get_p(); solve(); return 0; }
转载请注明原文地址: https://www.6miu.com/read-56344.html

最新回复(0)