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;
}
 
posted @ 2013-03-13 12:54  AbandonZHANG  阅读(112)  评论(0编辑  收藏  举报