您的位置:首页 > 其它

zoj3662 dp 离散优化

2016-05-14 20:36 337 查看
Problem:

让你求满足,和为n,且最小公倍数为m,的k个数的种数.

Analy:

首先很容想到dp[i][j][k]:前i个数,最小公倍数为j,和为k的状态定义.

转移的时候,就遍历第i个数后面加一个d(0<=d<=n),有转移方程:

dp[i+1][lcm(d,j)][k+d]+=dp[i][j][k]

但是这里维度肯定多了.

观察到lcm有一个性质,那就是n个数的lcm要为m,那么这n个数必须是m的约数,然后我们打表观察到

1000以内的数的约数最多有32个,就可以把第二维j离散化为只有32大小.

还有一个优化就是求lcm会多一个log的常数,我们需要打好所有约数相互lcm的表即可.

/***

***/

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
ll dp[2][33][1111];
int d[33], cnt, lcms[33][33];
int gcd(int a, int b) {
if (!b) return a;
return gcd(b, a % b);
}

int lcm(int x, int y) {
return x * y / gcd(x, y);
}

void pre(int x)
{
cnt = 0;
for (int i = 1; i <= x; i++) {
if (x % i == 0) d[cnt++] = i;
}
for (int i = 0; i < cnt; i++) {
for (int j = 0; j < cnt; j++) {
lcms[i][j] = lcm(d[i], d[j]);
}
}
}

int getid(int x) {
return lower_bound(d, d + cnt, x) - d;
}

int main(void)
{
int n, m, K;
while (cin >> n >> m >> K) {
pre(m);
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1;
for (int i = 0; i < K; i++) {
for (int j = 0; j < cnt; j++) {
for (int k = 0; k <= n; k++) {
if (dp[i % 2][j][k])
for (int l = 0; l < cnt; l++) {
if (k + d[l] <= n) {
dp[(i + 1) % 2][ getid(lcms[l][j]) ][k + d[l]] += dp[i % 2][j][k] % mod;
}
}
dp[i % 2][j][k] = 0;
}
}
}
cout << dp[K % 2][cnt - 1]
% mod << endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: