您的位置:首页 > 其它

hdu 5656 CA Loves GCD(dp)

2016-04-22 21:23 363 查看
题目的意思就是:
n个数,求n个数所有子集的最大公约数之和。

第一种方法:
枚举子集,求每一种子集的gcd之和,n=1000,复杂度O(2^n)。
谁去用?

所以只能优化!
题目中有很重要的一句话!

We guarantee that all numbers in the test are in the range [1,1000].


1

1

这句话对解题有什么帮助?
子集的种数有2^n种,但是,无论有多少种子集,它们的最大公约数一定在1-1000之间。
所以,我们只需要统计1-1000的最大公约数中可以有多少中方法凑成就可以了!

我们就会考虑dp。
官方题解:

我们令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 为出现过的数的最大值。


AC 代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
using namespace std;
#define N 1006
#define MOD 100000007
#define ll long long
ll n;
ll a
;
ll dp

,g

;
ll gcd(ll x,ll y){
return y==0?x:gcd(y,x%y);
}
void init(){
for(ll i=1;i<=1000;i++){
for(ll j=i;j<=1000;j++){
g[i][j]=g[j][i]=gcd(i,j);
}
}
}
int main()
{
init();
ll t;
scanf("%I64d",&t);
while(t--){
scanf("%I64d",&n);
for(ll i=0;i<n;i++){
scanf("%I64d",&a[i]);
}
memset(dp,0,sizeof(dp));

for(ll i=0;i<n;i++){
dp[i][a[i]]++;
for(ll j=1;j<=1000;j++){
dp[i+1][j]+=dp[i][j];
dp[i+1][j]%=MOD;

ll r=g[a[i+1]][j];
dp[i+1][r]+=dp[i][j];
dp[i+1][r]%=MOD;
}
}
ll ans=0;
for(ll i=1;i<=1000;i++){
if(dp
[i]!=0){
ans+=i*dp
[i];
ans%=MOD;
}
}
printf("%I64d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: