【BZOJ1025】游戏(SCOI2009)-数论+背包DP
测试地址:游戏
做法:本题需要用到数论+背包DP。
注意到题目中所给的是一个置换,一个置换中会有若干个循环,而题目中的排数就是这些循环长度的LCM,所以问题等价于求若干个和为的数,它们的LCM有多少种。
注意到对LCM不产生任何影响,所以这若干个数的和只需要即可(因为剩下的可以用去填)。而对于一些数,它们的LCM为各个质数在这些数中出现的最大次幂之积,我们注意到选几个合数的乘积是没有用的,因为它总是能被拆成LCM等价,但是和更小的质数幂的形式,于是现在的问题就是求取一些质数幂,使得它们的和,能组合成多少种LCM。那么我们令为考虑了前种质数,和为的方案数,我们发现这就是一个背包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;
}