Math Magic hdu 4427 (dp)

106 篇文章 0 订阅
42 篇文章 0 订阅

题意:

一个长度为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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值