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;
}

 

posted @ 2018-08-04 10:31  comld  阅读(201)  评论(0编辑  收藏  举报