题意:
一个长度为k的数量sum为n,lcm为m,问这样的数列能构造出多少个,结果对1e9+7取模。
解题思路:
计数问题,一般就是dp了。
首先想到的是,一个数列的lcm已知,那么数列 里所有的数应该都要是lcm的因子,不可能出现其它数,这一点想到,dp的优化其实也不难想了。
dp[i][j][k]表示长度为i,和为j,lcm为k的数列的方案数。
转移就是dp[i+1][j+a[i+1]][lcm(k,a[i+1])]+=dp[i][j][k];
然后第三维可以优化,我们不存实际的数,因为只可能出现lcm的因子, 所以我们离散化,第三维对应上因子的编号,1-1000的数的因子数量最多为31,所以空间上就优化很多了。
另外lcm我们需要先预处理,不然会超时。
然后就是,如果你也像我一样习惯把dp初始化为-1的话,那么答案对应的dp值要先置0.
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int dp[104][1004][33];
int fac[34];
int LCM[34][34];
int numf;
int ffind(int x)
{
return lower_bound(fac, fac+numf, x)-fac;
}
int lcm(int x, int y)
{
return x*y/__gcd(x, y);
}
int main()
{
int i, j, k, l, n, m, num, sum, x, y, z, e;
while(~scanf("%d%d%d", &n, &m, &num))
{
numf=0;
for(i=1; i<=m; i++)
{
if(m%i==0)
{
fac[numf++]=i;
// printf("%d\n", i);
}
}
for(i=0; i<numf; i++)
{
for(j=0; j<=i; j++)
{
LCM[i][j]=LCM[j][i]=ffind(lcm(fac[i], fac[j]));
}
}
for(i=0; i<=num; i++)for(j=0; j<=n; j++)for(k=0; k<numf; k++)dp[i][j][k]=-1;
dp[num][n][numf-1]=0;
for(i=0; i<numf; i++)
{
dp[1][fac[i]][i]=1;
}
for(i=1; i<num; i++)
{
for(j=0; j<=n; j++)
{
for(k=0; k<numf; k++)
{
if(dp[i][j][k]==-1)continue;
// printf("%d %d %d\n", i, j, k);
y=fac[k];
for(l=0; l<numf; l++)
{
x=fac[l];
if(x+j>n)continue;
e=LCM[k][l];
if(dp[i+1][x+j][e]==-1)dp[i+1][x+j][e]=0;
dp[i+1][x+j][e]=(dp[i+1][x+j][e]+dp[i][j][k])%mod;
}
}
}
}
printf("%d\n", dp[num][n][numf-1]%mod);
}
return 0;
}