[SCOI2009] 游戏
题面:Luogu
题解:
首先题目意思是这样的:
形式化的,给定\(n\),求
\[lcm(a_{1},a_{2},a_{3},...)
\]
的种类数,约束条件
\[\sum a_i=~n
\]
我们可以考虑对每种可能的\(lcm\),将其分解为质因数乘积,设
\[lcm=\prod p_i^{k_i}
\]
则必须要\(\sum p_i^{k_i} \le n\)
此时各个\(a\)互质,且和小于\(n\)的话可以用\(1\)补位
可以证明这样能取到所有种类
一个感性理解是:如果\(a\)不互质,则必然可以将一个\(gcd\)约掉,而这个少的部分可以用别的补上,因而必定不劣
设\(f_{i,j}\)表示前\(i\)个质数,总和为\(j\)的方案数
转移为\(f_{i,j}=\sum f_{i-1,j-p_i^{k}}~(k \ge 0,p_i^k\le j)\)
显然可以把空间压到只有第二维,即设\(f_j\)表示总和为\(j\),循环时从后往前即可
初始化\(f_0=1\)
#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
#define ll long long
#define maxn 1005
int prime[maxn],vis[maxn],pnum;
void eular(int maxnum=1000)
{
for(int i=2;i<=maxnum;++i)
{
if(!vis[i]) prime[++pnum]=i;
for(int j=1;j<=pnum&&i*prime[j]<=maxnum;++j)
{
vis[i*prime[j]]=1;
if(!(i%prime[j])) break;
}
}
}
ll dp[maxn];
int main()
{
int n;read(n);
eular(n);
dp[0]=1;
for(int i=1;i<=pnum;++i)
for(int j=n;j>=prime[i];--j)
for(int k=prime[i];k<=j;k*=prime[i])
dp[j]+=dp[j-k];
ll sum=0;
for(int i=0;i<=n;++i) sum+=dp[i];//全都是1也是一种方案,所以从0开始循环
printf("%lld\n",sum);
return 0;
}
一切伟大的行动和思想,都有一个微不足道的开始。
There is a negligible beginning in all great action and thought.
There is a negligible beginning in all great action and thought.