zoukankan      html  css  js  c++  java
  • ZOJ 3662 Math Magic (2012 Changchun Regional; LCM,DP)

    题目描述:给出K个数,使得这K个数的和为N,LCM为M,问有多少种.   裸DP啊……T T   设dp[i][j][k]表示取了i个数,和为j,LCM为k的时候的种数. 这样的话要开dp[100][1000][1000],需要优化. 首先第一维我们可以优化成滚动数组;其次,因为LCM为M,那么中间状态的LCM肯定为M的约数,而且加入的数也肯定是M的约数,所以我们预处理出LCM的约数,那么LCM和Ai只可能是那些约数,这样第三维又可以剪到40.最后开个dp[2][1000][40]就够了. 复杂度:O(100*1000*32*32)   (枚举第K个数*枚举当前和*枚举当前数的值*枚举LCM)  
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define MID(x,y) ((x+y)>>1)
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    
    int dp[2][1010][40];
    int LCM[1010][1010];
    bool tmp_ok[1000];
    int N, M, K;
    const int MOD = 1000000007;
    vector  divisor;
    int pos[1010];
    int gcd(int a, int b){
        return b ? gcd(b, a%b) : a;
    }
    int lcm(int a, int b){
        return a / gcd(a,b) * b;
    }
    void get_divisor(int num){
        mem(pos, -1);
        divisor.clear();
        for (int i = 1; i <= num; i ++){
            if (num % i == 0){
                divisor.push_back(i);
                pos[i] = divisor.size() - 1;
            }
        }
        return ;
    }
    int main(){
        mem(LCM, 0);
        for (int i = 1; i <= 1000; i ++)
            for (int j = 1; j <= 1000; j ++)
                LCM[i][j] = lcm(i, j);
        while(scanf("%d%d%d", &N, &M, &K) == 3){
            get_divisor(M);
            mem(dp, 0);
            dp[0][0][0] = 1;
            for (int i = 1; i <= K; i ++){
                mem(dp[i&1], 0);
                for (int j = 0; j <= N; j ++){
                    for (int p = 0; p < (int)divisor.size(); p ++){
                        //int tmp1 = divisor[p];
                        if (j + divisor[p] <= N){
                            for (int l = 0; l < (int)divisor.size(); l ++){
                                //int tmp2 = divisor[l];
                                if (dp[(i+1)&1][j][l] != 0){
                                    int new_l = LCM[divisor[p]][divisor[l]];
                                    if (new_l > M)  break;
                                    new_l = pos[new_l];
                                    dp[i&1][j+divisor[p]][new_l] += dp[(i+1)&1][j][l];
                                    if (dp[i&1][j+divisor[p]][new_l] >= MOD)
                                        dp[i&1][j+divisor[p]][new_l] -= MOD;
                                }
                            }
                        }
                    }
                }
            }
            printf("%d\n", dp[K&1][N][pos[M]]);
        }
    	return 0;
    }
    
     
    举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
  • 相关阅读:
    HDU 5222 ——Exploration——————【并查集+拓扑排序判有向环】
    nyoj 600——花儿朵朵——【离散化、线段树插线问点】
    Poj 3667——hotel——————【线段树区间合并】
    BNU 4260 ——Trick or Treat——————【三分求抛物线顶点】
    编写自己的Arduino库
    怎样看懂电路板?电路板短路检查方法是什么?
    Intel HEX格式
    关于2的补码
    sysfs是什么??
    Arduino 串口的一些高级用法
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/4114010.html
Copyright © 2011-2022 走看看