题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4885
题解: 对于M, M的约数集合A满足任取x, y ∈A, 有LCM(x, y)∈A, 于是,对于A中编号为i的元素x,设map[x] = i, 并有num[i] = x
于是,f[i][j][k]表示前i个数,和为j,LCM为num[i],递推式为 f[i + 1][j + x][map[LCM(x, k)]] += f[i][j][k]
然后预处理LCM(num[i], num[j]),记下标号
复杂度O(n * k * m) m为M以内的约数个数,实测m <= 50
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MOD 1000000007
using namespace std;
int lcm[50][50];
int f[3][1010][50];
int gcd(int a, int b)
{
if(b == 0) return a;
return gcd(b, a % b);
}
int lCM(int a, int b)
{
return a * b / gcd(a, b);
}
int cnt = 0;
int num[1000];
int map[1010];
int n, m, K;
int main()
{
//freopen("test.in", "r", stdin);
while(~scanf("%d%d%d", &n, &m, &K))
{
cnt = 0;
for(int i = 1; i <= m; i++)
{
if(m % i == 0) num[cnt++] = i;
}
//for(int i = 0; i < cnt; i++) cout << num[i] << " ";
for(int i = 0; i <= 1000; i++) map[i] = -1;
for(int i = 0; i < cnt; i++) map[num[i]] = i;
for(int i = 0; i < cnt; i++)
for(int j = 0; j < cnt; j++)
lcm[i][j] = map[lCM(num[i], num[j])];
memset(f, 0, sizeof f);
f[0][0][0] = 1;
for(int i = 0; i < K; i++)
{
int ii = i & 1, jj = (i + 1) & 1;
memset(f[jj], 0, sizeof(f[jj]));
for(int j = 0; j < n; j++)
for(int k = 0; k < cnt; k++)
if(f[ii][j][k])
{
for(int p = 0; p < cnt; p++)
{
int tmp = lcm[k][p];
if(tmp == -1) break;
if(j + num[p] > n) break;
f[jj][j + num[p]][tmp] += f[ii][j][k];
if (f[jj][j + num[p]][tmp] >= MOD) f[jj][j + num[p]][tmp] -= MOD;
/*
if(lcm[num[k]][num[p]] > m) break;
if(j + num[p] > n) break;
f[jj][j + num[p]][lcm[num[k]][num[p]]] += f[ii][j][num[k]];
if (f[jj][j + num[p]][lcm[num[k]][num[p]]] >= MOD) f[jj][j + num[p]][lcm[num[k]][num[p]]] -= MOD;
*/
}
}
}
printf("%d\n", f[K & 1][n][map[m]]);
}
return 0;
}