HDU 5656 CA Loves GCD (BestCoder Round #78) DP

来源:互联网 发布:软件项目介绍书 编辑:程序博客网 时间:2024/05/19 01:29

CA Loves GCD

Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)

问题描述

CA喜欢是一个热爱党和人民的优秀同♂志,所以他也非常喜欢GCD(请在输入法中输入GCD得到CA喜欢GCD的原因)。
现在他有N个不同的数,每次他会从中选出若干个(至少一个数),求出所有数的GCD然后放回去。
为了使自己不会无聊,CA会把每种不同的选法都选一遍,CA想知道他得到的所有GCD的和是多少。
我们认为两种选法不同,当且仅当有一个数在其中一种选法中被选中了,而在另外一种选法中没有被选中。

输入描述

第一行 T,表示有 T组数据。
接下来 T 组数据,每组数据第一行一个整数 N,表示CA的数的个数,接下来一行 N 个整数 Ai
​​ 表示CA的每个数。
1T50, 1N1000, 1Ai1000

输出描述

对于每组数据输出一行一个整数表示CA所有的选法的GCD的和对 100000007 取模的结果。

输入样例

2
2
2 4
3
1 2 3

输出样例

8
10


解题思路

官方题解

By YJQ
我们令dp[i][j]表示在前i个数中,选出若干个数使得它们的gcd为j的方案数,于是只需要枚举第i+1个数是否被选中来转移就可以了

令第i+1个数为v,当考虑dp[i][j]的时候,我们令

dp[i+1][j]+=dp[i][j],dp[i+1][gcd(j,v)]+=dp[i][j]

复杂度O(N*MaxV),MaxV 为出现过的数的最大值

其实有O(MaxV*log(MaxV))的做法,我们考虑记f[i]表示从这些数中选择若干个数,使得他们的gcd是i的倍数的方案数。假如有K个数是i的倍数,则f[i]=2^K-1,再用g[i]表示从这些数中选择若干个数,使得他们的gcd是i的方案数,则g[i]=f[i] - g[j] (对于所有j是i的倍数)。

由调和级数可以得到复杂度为O(MaxV *log(MaxV))

两种方法,我按第一种写了一下代码,挂到博客上回顾用。

代码

#include <cstdio>#include <cstring>using namespace std;int dp[1001][1001], buf[1001][1001];int gcd(int a, int b) {  //记忆化gcd    if (buf[a][b])return buf[a][b];    if (b == 0)return buf[a][b] = a;    return buf[a][b] = buf[b][a % b] = gcd(b, a % b);}int main() {    int tt, n, maxnum = 0, t;    int a[1001];    scanf("%d", &tt);    while (tt--) {        maxnum = 0;        scanf("%d", &n);        memset(dp, 0, sizeof(int) * 1001 * 1001);        for (int i = 1; i <= n; i++) {            scanf("%d", &a[i]);            if (a[i] > maxnum)maxnum = a[i];        }        for (int i = 1; i <= n; i++) {            dp[i][a[i]] = 1;            for (int j = maxnum; j >= 1; --j) {                dp[i][j] = (dp[i][j] + dp[i - 1][j]) % 100000007;                if (dp[i - 1][j])  //这里可以节省很多时间                    dp[i][gcd(a[i],j)] = (dp[i][gcd(a[i],j)] + dp[i - 1][j]) % 100000007;            }        }        long long ans = 0;        for (int i = 1; i <= maxnum; ++i) {            ans = (ans + (long long)dp[n][i] * (long long)i) % 100000007;        }        printf("%I64d\n", ans);    }    return 0;}
1 0