链接:点击打开链接
题意:求k个数和为n,最小公倍数为m的种数
代码:
#include <set>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const long long MOD=1000000007;
vector<long long> fac;
long long lcm[1005][1005];
long long dp[2][1005][1005];
long long gcd(long long a,long long b){
if(b==0)
return a;
return gcd(b,a%b);
}
int main(){
long long n,m,num,i,j,k,p,q,tmp,len;
for(i=1;i<=1000;i++)
for(j=1;j<=1000;j++)
lcm[i][j]=i/gcd(i,j)*j;
while(scanf("%I64d%I64d%I64d",&n,&m,&num)!=EOF){
fac.clear(); //dp[i][j][k]表示i个数,和为j,最小公倍数是k的种数
for(i=1;i<=m;i++) //直接进行转移一定会超时,因此先处理出j的所欲因子
if(m%i==0)
fac.push_back(i);
len=fac.size();
memset(dp,0,sizeof(dp));
for(i=0;i<len;i++) //因为最小公倍数是m,所以k个数每一个都是m的因子
dp[1][fac[i]][fac[i]]=1; //所以直接在因子中间转移
for(i=1;i<num;i++){
for(p=i;p<=n;p++)
for(q=0;q<len;q++)
dp[(i+1)%2][p][fac[q]]=0; //不能用memset
for(j=i;j<=n;j++){
for(p=0;p<len;p++){
if(dp[i%2][j][fac[p]]==0)
continue;
for(q=0;q<len&&fac[q]+j<=n;q++){
tmp=lcm[fac[p]][fac[q]];
dp[(i+1)%2][j+fac[q]][tmp]=(dp[(i+1)%2][j+fac[q]][tmp]+dp[i%2][j][fac[p]])%MOD;
}
}
}
}
printf("%I64d\n",dp[num%2][n][m]);
}
return 0;
}