zoj 3662

嘿嘿,后天就期中考试了,不过鸭梨不算大,复习的也差不多了,再加上根本没心思看,话说现在对学校的功课真的是越来越厌烦了。。。今天还不错,刷了2个题,还都是现场赛的题。。。

这题其实早就像做了,就是今年东北赛区的H题(幸亏没有去,要不然肯定是铁)下午的时候在zoj上做了模拟赛,过掉K题时,我就开始想这题。。后来怎么也没有想出来,不过猜测这是个DP。。后来听学长说,果断是。。看了@zz_1215的代码,读懂了状态方程,很简单,方法如下:

f[i][j][k]表示选i个数,总和为j,最小公倍数为k。。由于题目要求选K0个数最小公倍数为M,所以这其中任意数的最小公倍数一定是M的约数。。这样就可以写出转移方程,看代码。。。这个题有意思的是,要从后往前推,这也就是话说中的记忆化搜索吧。。。写完后发现TLE...杯具。。怎么办?两个优化,一个是K一定为M的约数,压缩一下状态。。最重要的是要预处理,实现计算和各个约数之间的最小公倍数。。。(话说本菜总没有这个意识。。。)果断AC,附代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>

#define read(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define cl(a) memset(a,0,sizeof(a))
#define mod 1000000007
#define M 1005
#define D 50
#define N 104

using namespace std;

int f[N][M][D],num[D],hash[M],m,cnt,g[D][D];

void div()
{
    int i;
    cl(num);cl(hash);
    for (i=1;i<=m;i++)
        if (m%i==0)
        { 
            ++cnt;
            hash[i]=cnt;
            num[cnt]=i; 
        }
}

int gcm(int p,int q)
{
    if (p<q) return gcm(q,p);
    if (p%q==0) return q;
    else return gcm(q,p%q);
}

int lcm(int p,int q)
{
    return p/gcm(p,q)*q;
}

void init()
{
    int i,j,x,y;
    cl(f);
    for (i=1;i<=cnt;i++) 
    {
        x=num[i];
        f[1][x][hash[x]]=1;
        for (j=1;j<=cnt;j++)
        {
            y=num[j];
            g[i][j]=lcm(x,y);
        }
    }
}

int main()
{
    int n,k0,i,j,k,r;
    while (read(n,m,k0)!=EOF)
    {
        cnt=0;
        div();
        init();
        for (i=1;i<k0;i++)
            for (j=i;j<n;j++)
                for (k=1;k<=cnt&&j+num[k]<=n;k++)
                    for (r=1;r<=cnt;r++)
                        if (f[i][j][r]>0)
                        {
                            int x,t;
                            x=num[k];
                            t=hash[g[r][k]];
                            f[i+1][j+x][t]=(f[i+1][j+x][t]+f[i][j][r])%mod;
                        }
        printf("%d\n",f[k0][n][hash[m]]);
    }                               
    return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值