题意:
给你k个数的和和k个数的最小公倍数。问你一共有多少满足条件的解。
用三维来表示状态。
dp[i][j][k]。表示长度为i。和为j。最小公倍数为k的方法数。
设a为解的第i+1个数。
那么状态转移就为
dp[i+1][j+a][lcm(a,k)]+=dp[i][j][k]。
lcm为最大公倍数。
由于三维开不下就只能用滚动数组了。
为了节约时间先预处理出1000以内任意两数的最小公倍数。注意一点。一个数学常识。如果这k个数的最小公倍数是
x。那么这k个数肯定都是x的因子。所以先预处理出x的因子。这样就可以有效的减少枚举范围。还有memset要慎用尤其是在这种数量级下。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cmath> 5 #include <memory.h> 6 using namespace std; 7 typedef long long ll; 8 const int mod = 1e9 + 7; 9 const ll maxn = 1e5 + 10; 10 int dp[2][1005][1005],fac[1005],cnt,lcm[1005][1005]; 11 int gcd(int a,int b){ 12 return b == 0?a:gcd(b,a%b); 13 } 14 void init(){ 15 for(int i = 1;i <= 1000;i++) 16 for(int j = i;j <= 1000;j++) 17 lcm[i][j] = lcm[j][i] = i*j/gcd(i,j); 18 } 19 int n,m,k; 20 int main() { 21 //freopen("in.txt","r",stdin); 22 init(); 23 while(~scanf("%d%d%d",&n,&m,&k)){ 24 memset(dp,0,sizeof(dp)); 25 memset(fac,0,sizeof(fac)); 26 cnt = 0; 27 for(int i = 1;i <= m;i++) 28 if(m % i == 0) fac[++cnt] = i; 29 int id = 0; 30 for(int i = 1;i <= cnt;i++) 31 dp[id][fac[i]][fac[i]] = 1; 32 for(int i = 1;i <= k - 1;i++){ 33 for(int t1 = 1;t1 <= n;t1++) 34 for(int t2 = 1;t2 <= cnt;t2++) 35 dp[id^1][t1][fac[t2]] = 0; 36 for(int j = 1;j <= n;j++){ 37 for(int t = 1;t <= cnt;t++){ 38 if(!dp[id][j][fac[t]]) continue; 39 for(int x = 1;x <= cnt;x++){ 40 if(j + fac[x] > n) break; 41 dp[id^1][j+fac[x]][lcm[fac[t]][fac[x]]] += dp[id][j][fac[t]]; 42 dp[id^1][j+fac[x]][lcm[fac[t]][fac[x]]] %= mod; 43 } 44 45 } 46 47 } 48 id ^= 1; 49 } 50 printf("%d ",dp[id][n][m]); 51 } 52 53 54 return 0; 55 }