CA Loves GCD

Description

CA is a fine comrade who loves the party and people; inevitably she loves GCD (greatest common divisor) too. 
Now, there are $N$ different numbers. Each time, CA will select several numbers (at least one), and find the GCD of these numbers. In order to have fun, CA will try every selection. After that, she wants to know the sum of all GCDs. 
If and only if there is a number exists in a selection, but does not exist in another one, we think these two selections are different from each other.

Input

First line contains $T$ denoting the number of testcases.
$T$ testcases follow. Each testcase contains a integer in the first time, denoting $N$, the number of the numbers CA have. The second line is $N$ numbers. 
We guarantee that all numbers in the test are in the range [1,1000].
$1 \le T \le 50$

Output

$T$ lines, each line prints the sum of GCDs mod $100000007$.

Sample Input

2
2
2 4
3
1 2 3

Sample Output

8
10

题意:给你n个数ai,n和ai范围在1到1000之间,问这些数任意不重复组合的最大公约数之和取模100000007的结果。 

思路:设dp[i][j]表示前i个数里任意组合后的最大公约数为j的个数,后面就像一个背包问题,对于每个a[i],背包我们只考虑两种情况,但是这里我们需要考虑三种情况,dp[i][j]分别为a[i]独自一个作为集合时,a[i]不加入dp[i-1][j]集合,此时dp[i][j]完全由dp[i-1][j]得来,以及将a[i]加入dp[i-1][j]集合时,虽然有三种状态,但是我们需要全部累加起来,所以状态转移方程有三步,dp[i][a[i]] = 1;

dp[i][j] = (dp[i][j]+dp[i-1][j])%M;dp[i][gcd[a[i]][j]] = (dp[i][gcd[a[i]][j]]+dp[i-1][j])%M;最后统计答案时这里需要注意一下数据范围,不然会超出int范围,还有最坑的一个点就是这里模数M不是1e9+7,而是1e8+7,这个是最坑的一个地方,还有需要注意的就是要先把1-1000之间任意两个数之间的gcd先打表,不然会超时。

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int M = 1e8 + 7;
const int maxn = 1000 + 10;
int a[maxn],  gcd[maxn][maxn];
long long dp[maxn][maxn];
int Gcd(int a, int b)
{
    if(b == 0) return a;
    return Gcd(b, a%b);
}
void init()
{
    for(int i = 1; i <= 1005; i ++)
    {
        for(int j = 1; j <= 1005; j ++)
        {
            gcd[i][j] = Gcd(i, j);
        }
    }
}
int main()
{
    init();
    int T;
    scanf("%d", &T);
    while(T --)
    {
        int n, gmax = -1;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++ i)
        {
            scanf("%lld", &a[i]);
            gmax = max(gmax, a[i]);
        }
        memset(dp, 0, sizeof(dp));
        //cout << gmax << endl;
        for(int i = 1; i <= n; ++ i)
        {
            dp[i][a[i]] = 1;
            for(int j = gmax; j >= 1; -- j)
            {
                dp[i][j] = (dp[i][j]+dp[i-1][j])%M;
                dp[i][gcd[a[i]][j]] = (dp[i][gcd[a[i]][j]]+dp[i-1][j])%M;
            }
        }
        long long ans = 0;
        for(int i = 1; i <= gmax; ++ i)
        {
            ans = (ans + (long long)dp[n][i]*i)%M;
        }
//        for(int i = 1; i <= n; ++ i)
//        {
//            for(int j = 1; j <= gmax; j ++)
//            {
//                cout << dp[i][j] << " ";
//            }
//            cout << endl;
//        }
        printf("%lld\n", ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值