1.题目描述:点击打开链接
2.解题思路:本题要求寻找k个正整数,它们的和恰好是N,它们的LCM恰好是M的解的个数。可以设置一个三维的dp来解决。用dp(i,j,k)表示选择i个数,它们的和恰好是j,它们的LCM恰好是k的个数。那么答案就是dp(k,n,m)。不过这里介绍一种利用状态压缩思想求解的方法。
通过题意可以发现,N,M的范围都比较小,不超过1000,而1000之内的所有数的不同素因子的种类数目不超过4个,这是因为2*3*5*7<1000,而2*3*5*7*11>1000。考虑到素因子种类数非常少的特点,我们可以考虑状态压缩。设dp(i,j,s)表示选择i个数,它们的和恰好是j,它们的不同素因子(这里考虑的素因子都是它的幂次恰好和M分解后对应的幂次相等的那个素因子)构成的集合为s时的解的个数。可以利用刷表法来求解,刷新公式如下:
dp(i,j+d[i],k|s[i])=dp(i,j+d[i],k|s[i])+dp(i-1,j,k);
上式中,d[i]表示M的第i个约数,s[i]表示第i个约数的“合格”的素因子构成的集合。假设M一共有L种不同的素因子,那么,k个整数的LCM恰好等于M就等价于dp(k,N,2^L-1)。2^L-1就是这L种素因子构成的集合的全集。这样,本题便可以被顺利的解决。
3.代码:
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;
const int MOD=1e9+7;
const int N=1050;
int dp[110][N][1<<4];
int primes[N];
int vis[N];
int idx;
void init() //筛素数
{
me(vis);
int m=sqrt(N+0.5);
for(int i=2;i<=m;i++)
if(!vis[i])
for(int j=i*i;j<N;j+=i)
vis[j]=1;
for(int i=2;i<N;i++)
if(!vis[i])
primes[idx++]=i;
}
int main()
{
init();
int n,m,k;
int num[5],p[5],d[233],type[233]; //p[i]表示M的第i个素因子,num[i]表示该素因子的幂次,d[i]表示M的第i个约数,type[i]表示第i个约数的“合格的”素因子构成的集合
int l,tot; //l表示M一共有l种不同的素因子,tot表示M一共有tot个约数
while(~scanf("%d%d%d",&n,&m,&k))
{
l=0;
for(int i=0,j=m;primes[i]<=j;i++)//对M进行分解
if(j%primes[i]==0)
{
p[l]=primes[i];
num[l]=0;
while(j%primes[i]==0)j/=primes[i],num[l]++;
l++;
}
tot=0;
for(int i=1;i<=m;i++) //寻找M的所有约数
if(m%i==0)
{
d[tot]=i;
type[tot]=0;
int tmp=i,cnt;
for(int j=0;j<l;j++)
if(tmp%p[j]==0)
{
cnt=0;
while(tmp%p[j]==0)tmp/=p[j],cnt++;
if(cnt==num[j]) //如果该约数分解后,某一项的幂次和M对应的幂次相等,说明是一个合法的素因子,加入集合
type[tot]|=1<<j;
}
tot++;
}
int up=1<<l; //up表示M的不同素因子构成的全集
me(dp);
for(int i=0;i<tot&&d[i]<=n;i++)
dp[1][d[i]][type[i]]=1; //刷新时候需要的基础的解
for(int i=2;i<=k;i++)
for(int j=1;j<=n;j++)
for(int k=0;k<up;k++)//枚举所有集合
if(dp[i-1][j][k])
for(int kk=0;kk<tot&&d[kk]+j<=n;kk++)//枚举所有可能的约数,进行刷新
dp[i][j+d[kk]][k|type[kk]]=( dp[i][j+d[kk]][k|type[kk]]+dp[i-1][j][k])%MOD;
printf("%d\n",dp[k][n][up-1]);
}
}