SCOI2009游戏 (数论+dp)
题解
很显然,对于一个确定的排列,每个数字的移动规则是一定的,我们根据这个排列,把它抽象为i向a[i]连一条边,很显然最后会构成一个环,那么行数就是这些环长的lcm。
那么问题变成了把n任意进行划分,求它们能够组成的lcm的个数。
我们发现,只有素数会对答案有影响,所以我们就对每个素数以及它们的幂跑一边01背包,最后统计答案即可。
代码
#include<iostream> #include<cstdio> #define N 1009 using namespace std; long long dp[N][N],prime[N],vis[N],n,tot,k; int main() { scanf("%d",&n); for(int i=2;i<=n;++i) { if(!vis[i])prime[++tot]=i; for(int j=1;j<=tot&&(k=i*prime[j])<=n;++j) { vis[k]=1;if(i%prime[j]==0)break; } } dp[0][0]=1; for(int i=1;i<=tot;++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=0;k+j<=n;++k) dp[i][j+k]+=dp[i-1][k]; } long long ans=0; for(int i=0;i<=n;++i)ans+=dp[tot][i]; cout<<ans; return 0; }