123789456ye

已AFO

[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;
}
posted @ 2020-04-20 17:25  123789456ye  阅读(144)  评论(0编辑  收藏  举报