【bzoj1025】[SCOI2009]游戏

2015.04.22 10:17 Wed | 22次阅读 | 旧日oi | 固定链接 | 源码

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 。

题解

先写的暴力,写着写着发现可以改成搜索每个质因数的次数,然后写着写着又想到了dp……
我们要求的就是把n分成一些正整数,求这些数的最小公倍数,统计有多少个不同的最小公倍数
用dp[i][j]表示用前1个质因数表示和为j的
dp[i][j]表示用前i个质因数表示和为j的数的方案数。
则dp[i][j]=sigma(dp[i-1][j-prime[i]*k])+dp[i-1][j]。
答案就是sigma(dp[prime_num][i])(0<=i<=n)

我的程序

#include <bits/stdc++.h>
using namespace std;
int n;
int prime[1005],cnt;
bool vis[1005];
long long dp[1005][1005],ans;
int main()
{
    cin>>n;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i]) prime[++cnt]=i;
        for(int j=1;j<=cnt&&prime[j]*i<=n;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
    dp[0][0]=1;
    for(int i=1;i<=cnt;i++)
    {
        for(int j=0;j<=n;j++) dp[i][j]=dp[i-1][j];
        for(int j=prime[i];j<=n;j*=prime[i])
        {
            for(int k=j;k<=n;k++)
            dp[i][k]+=dp[i-1][k-j];
        }
    }
    for(int i=0;i<=n;i++) ans+=dp[cnt][i];
    cout<<ans<<endl;
}```