HDU - CA Loves GCD

1.题面

http://acm.hdu.edu.cn/showproblem.php?pid=5656

2.题意

给你一堆数,对于这堆数的所有组合,(如{1,2},有{1},{2},{1,2}三种组合)求出所有组合的最大公约数之和。

3.解题思路

用动态规划做,用dp[i][j]表示已经选择了i个数,其中最大公约数为j的数字有dp[i][j]个。

则dp[i+1][j]包含所有dp[i][j]的组合,同时需要加入所有前面的组合与加入j以后的情况。

设置dp[0][0] = 1;

为初始情况,那么每次添加i时i与i的gcd情况都可以通过i与0的gcd来添加一个dp[i][num[i]]++;

简直妙手!

具体的看代码,我用了滚动数组。

4.解题代码

/*****************************************************************
    > File Name: tmp.cpp
    > Author: Uncle_Sugar
    > Mail: uncle_sugar@qq.com
    > Created Time: 2016年04月02日 星期六 19时42分02秒
*****************************************************************/
# include <cstdio>
# include <cstring>
# include <cmath>
# include <cstdlib>
# include <climits>
# include <iostream>
# include <iomanip>
# include <set>
# include <map>
# include <vector>
# include <stack>
# include <queue>
# include <algorithm>
using namespace std;

const int debug = 0;
const int size  = 1000 + 10; 
const int INF = INT_MAX>>1;
const int MOD = 100000007;
typedef long long ll;

int gcd[size][size];
int getGcd(int x,int y){
    if (gcd[x][y]==0){
        gcd[y][x] = gcd[x][y] = (y==0?x:getGcd(y,x%y));
    }
    return gcd[x][y];
}
int num[size];
int dp1[size];
int dp2[size];
int main()
{
    std::ios::sync_with_stdio(false);cin.tie(0);
    int i,j;
    int T;
    cin >> T;
    while (T--){
        int n;
        cin >> n;
        int maxsum = -1;
        for (i=0;i<n;i++){
            cin >> num[i];
            maxsum = max(maxsum,num[i]);
        }
        int *guest = dp1;
        int *host = dp2;
        int *tmp;
        memset(host,0,sizeof(int)*size);
		host[0] = 1;
        for (i=0;i<n;i++){
            for (j=0;j<=maxsum;j++)
                guest[j] = host[j];
            for (j=0;j<=maxsum;j++){
                if (host[j]>0){
                    int G = getGcd(j,num[i]);
                    guest[G] += host[j];
                    guest[G] %= MOD;
                }
            }
            tmp = guest;guest = host;host = tmp;
        }
        ll ans = 0;
        for (i=0;i<=maxsum;i++){
            ans += (ll)host[i]*i;
            ans %= MOD;
        }
        cout << ans << '\n';
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值