【BZOJ1025】游戏(SCOI2009)-数论+背包DP

测试地址:游戏
做法:本题需要用到数论+背包DP。
注意到题目中所给的是一个置换,一个置换中会有若干个循环,而题目中的排数就是这些循环长度的LCM+1,所以问题等价于求若干个和为N的数,它们的LCM有多少种。
注意到1对LCM不产生任何影响,所以这若干个数的和只需要N即可(因为剩下的可以用1去填)。而对于一些数,它们的LCM为各个质数在这些数中出现的最大次幂之积,我们注意到选几个合数的乘积是没有用的,因为它总是能被拆成LCM等价,但是和更小的质数幂的形式,于是现在的问题就是求取一些质数幂,使得它们的和N,能组合成多少种LCM。那么我们令f(i,j)为考虑了前i种质数,和为j的方案数,我们发现这就是一个背包DP,状态转移方程很容易写出,于是我们就完成了这一题。
(我校提高组模拟赛出了这题,我居然现在看了题解才会做,我好弱啊……)
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,prime[1010]={0};
ll f[1010]={0};
bool vis[1010]={0};

void calc_prime()
{
    prime[0]=0;
    for(int i=2;i<=n;i++)
    {
        if (!vis[i]) prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++)
        {
            vis[i*prime[j]]=1;
            if (i%prime[j]==0) break;
        }
    }
}

int main()
{
    scanf("%d",&n);
    calc_prime();

    f[0]=1;
    for(int i=1;i<=prime[0];i++)
    {
        for(int j=n;j>=0;j--)
        {
            int k=prime[i];
            while(k<=j)
            {
                f[j]+=f[j-k];
                k*=prime[i];
            }
        }
    }
    ll ans=0;
    for(int i=0;i<=n;i++)
        ans+=f[i];
    printf("%lld",ans);

    return 0;
}
posted @ 2018-05-14 09:28  Maxwei_wzj  阅读(66)  评论(0编辑  收藏