ZOJ 3662 Math Magic 分阶段的动态规划 (+离散化) ★★★☆

Math Magic

Time Limit: 3 Seconds       Memory Limit: 32768 KB

Yesterday, my teacher taught us about math: +, -, *, /, GCD, LCM... As you know, LCM (Least common multiple) of two positive numbers can be solved easily because of a * b = GCD (a, b) * LCM (a, b).

In class, I raised a new idea: "how to calculate the LCM of K numbers". It's also an easy problem indeed, which only cost me 1 minute to solve it. I raised my hand and told teacher about my outstanding algorithm. Teacher just smiled and smiled...

After class, my teacher gave me a new problem and he wanted me solve it in 1 minute, too. If we know three parameters N, M, K, and two equations:

1. SUM (A1, A2, ..., Ai, Ai+1,..., AK) = N 
2. LCM (A1, A2, ..., Ai, Ai+1,..., AK) = M

Can you calculate how many kinds of solutions are there for Ai (Ai are all positive numbers). I began to roll cold sweat but teacher just smiled and smiled.

Can you solve this problem in 1 minute?

Input

There are multiple test cases.

Each test case contains three integers N, M, K. (1 ≤ N, M ≤ 1,000, 1 ≤ K ≤ 100)

Output

For each test case, output an integer indicating the number of solution modulo 1,000,000,007(1e9 + 7).

You can get more details in the sample and hint below.

Sample Input
4 2 2
3 2 2
Sample Output
1
2
Hint

The first test case: the only solution is (2, 2).

The second test case: the solution are (1, 2) and (2, 1).


The 2012 ACM-ICPC Asia Changchun Regional Contest


担心超时、担心wa的,竟然一发给过了。

题目要求的k个数看作k个阶段。

每个阶段的lcm必须是给出M(LCM)的因子。把它存到map里,对应为fy

dp[x][fy][sum] 表示第x阶段、map[lcm]=fy、和为sum的种类数。

为了能计算出最后的种数,

分阶段是关键,先要枚举K个位置,然后对每个位置上枚举前面数之和,还有加入下个位置的数。

(从左往右依次枚举)

求出lcm原来  和  lcm现在。然后就是对sum进行枚举,进行状态转移。


   dp[i+1][fy][sum+delta]=(dp[i+1][fy][sum+delta]+dp[i][j][sum])%mod;

(i表示考虑了前i个数,delta表示新加的数,sum表示第1到第i个数之和,j就是原来的lcm在map映射之后的值,

fy是新的lcm在map映射之后的值)。(此处可能看不懂,具体看代码,没多少)

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
#include<vector>
#include<map>
#include<sstream>
#include<set>
#include<stack>
#include<cctype>
#include<utility>
#pragma comment(linker, "/STACK:102400000,102400000")
#define PI (4.0*atan(1.0))
#define eps 1e-10
#define sqr(x) ((x)*(x))
#define FOR0(i,n)  for(int i=0 ;i<(n) ;i++)
#define FOR1(i,n)  for(int i=1 ;i<=(n) ;i++)
#define FORD(i,n)  for(int i=(n) ;i>=0 ;i--)
#define  lson   ind<<1,le,mid
#define rson    ind<<1|1,mid+1,ri
#define MID   int mid=(le+ri)>>1
#define zero(x)((x>0? x:-x)<1e-15)
#define mk    make_pair
#define _f     first
#define _s     second
using namespace std;
//const int INF=    ;
typedef long long ll;
//const ll inf =1000000000000000;//1e15;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//fin.close();
//fout.close();
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
const int INF =0x3f3f3f3f;
//const int maxn=    ;
//const int maxm=    ;

int SUM,LCM,V;
vector<int >ve;
map<int,int >mp;
const int mod=1000000007;
int lcm(int a,int b)
{
    return a/__gcd(a,b)*b;
}

void pre()
{
    ve.clear();
    mp.clear();
    for(int i=1;i<=LCM;i++)
    {
        if(LCM%i)  continue;
        ve.push_back(i);
        int m=mp.size();
        mp[i]=m;
    }
}

int dp[103][50][1010];//v   lcm  sum
int main()
{
   while(~scanf("%d%d%d",&SUM,&LCM,&V))
   {
       pre();
       memset(dp,0,sizeof dp);

       for(int i=0;i<ve.size();i++)
       {
           int x=ve[i];
           dp[1][i][x]=1;
       }


       for(int i=1;i<V;i++)
       {
           for(int j=0;j<ve.size();j++)
           {
               int x=ve[j];

              for(int k=0;k<ve.size();k++)//新加入物品
              {
                  int delta=ve[k];
                  int y=lcm(x,delta);
                   if(!mp.count(y))  continue;
                  int fy=mp[y];
                  for(int sum=0;sum+delta<=SUM;sum++)
                {
                       dp[i+1][fy][sum+delta]=(dp[i+1][fy][sum+delta]+dp[i][j][sum])%mod;
                }

              }


           }
       }

       printf("%d\n",dp[V][mp[LCM]][SUM]);
   }

    return 0;
}
    


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值