題意:已知N,M,K,求滿足如下公式的{A1,A2,A3,...,Ak}有多少個?
1. SUM (A1,
A2, ..., Ai, Ai+1,...,
AK) = N
2. LCM (A1, A2,
..., Ai, Ai+1,..., AK)
= M
分析:很自然的可以想到用DP來做,但狀態方程想到的是dp[i][j][k],代表加上第i個數後總數爲j最小公倍數爲k的種類數,這樣的話內存就不允許了,不過在自己模擬狀態過程轉換時發現,最小公倍數這維有許多狀態是用不上的,比如給你一個m=5,狀態過程中,只會用到1、5,其他數你沒有辦法得到最小公倍數爲5,所以可以認爲只要能夠整除即可,這樣就能將1000哈希成連50都不到的。由此,問題即可解決。
代碼如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1005;
const int MOD = 1000000007;
int dp[105][maxn][40];
int lcc[50];
int next_lcc[50][50];
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int lcm(int a,int b){
return a/gcd(a,b)*b;
}
int main(){
int n,m,K;
while(~scanf("%d %d %d",&n,&m,&K)){
int num=0;
for(int i=1;i<=m;i++){
if(!(m%i)) lcc[num++]=i;
}
for(int i=0;i<K;i++){
for(int j=0;j<=n;j++){
for(int k=0;k<num;k++){
dp[i][j][k]=0;
}
}
}
for(int i=0;i<num;i++)
for(int j=0;j<num;j++){
next_lcc[i][j]=-1;
}
for(int i=0;i<num;i++){
for(int j=0;j<num;j++){
int tmp=lcm(lcc[i],lcc[j]);
if(tmp>m) break;
for(int k=0;k<num;k++){
if(lcc[k]==tmp)
next_lcc[i][j]=k;
}
}
}
for(int i=0;i<num;i++){
dp[0][lcc[i]][i]=1;
}
for(int i=1;i<K;i++){
for(int j=0;j<num;j++){
for(int s=1;s<=n;s++){
int new_s=s+lcc[j];
if(new_s>n) break;
for(int k=0;k<num;k++){
if(next_lcc[j][k]==-1) break;
dp[i][new_s][next_lcc[j][k]]+=dp[i-1][s][k];
dp[i][new_s][next_lcc[j][k]]%=MOD;
}
}
}
}
printf("%d\n",dp[K-1][n][num-1]);
}
}