题意:
问题描述
CA喜欢是一个热爱党和人民的优秀同♂志,所以他也非常喜欢GCD(请在输入法中输入GCD得到CA喜欢GCD的原因)。
现在他有N个不同的数,每次他会从中选出若干个(至少一个数),求出所有数的GCD然后放回去。
为了使自己不会无聊,CA会把每种不同的选法都选一遍,CA想知道他得到的所有GCD的和是多少。
我们认为两种选法不同,当且仅当有一个数在其中一种选法中被选中了,而在另外一种选法中没有被选中。
思路:比赛的时候没做出来….(我真是太弱了
根据官方题解做的ORZ
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<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) (x&(-x))
typedef long long LL;
#define maxn 1005
#define mod 100000007
const int inf=(1<<28)-1;
int G[maxn][maxn];
int Gcd(int a,int b)
{
if(G[a][b]) return G[a][b];
if(!a) return b;
return Gcd(b%a,a);
}
void get_gcd()
{
for(int i=1;i<maxn;++i)
for(int j=1;j<maxn;++j)
G[i][j]=Gcd(i,j);
}
int A[maxn];
LL dp[maxn][maxn];
int main()
{
get_gcd();
int T;
scanf("%d",&T);
while(T--)
{
int n,maxs=0;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&A[i]);
maxs=max(maxs,A[i]);
}
mem(dp,0);
for(int i=1;i<=n;++i)
{
dp[i][A[i]]=1;
for(int j=1;j<=maxs;++j)
if(dp[i-1][j])
{
dp[i][j]=(dp[i][j]+dp[i-1][j])%mod;
dp[i][G[j][A[i]]]=(dp[i][G[j][A[i]]]+dp[i-1][j])%mod;
}
}
LL ans=0;
for(int i=1;i<=maxs;++i)
{
ans=(ans+i*dp[n][i])%mod;
}
printf("%lld\n",ans);
}
return 0;
}
思路二:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) (x&(-x))
typedef long long LL;
#define maxn 1005
const LL mod=100000007;
const int inf=(1<<28)-1;
LL dp[maxn];
int A[maxn],cal[maxn];
LL quick_pow(LL a,LL b,LL m)
{
LL res=1,tmp=a;
while(b)
{
if(b&1) res=(res*tmp)%m;
tmp=(tmp*tmp)%m;
b/=2;
}
return res;
}
LL max(LL a,LL b)
{
if(a>b) return a;
return b;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
mem(cal,0);
int n;
int maxs=0;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&A[i]);
cal[A[i]]++;
maxs=max(A[i],maxs);
}
for(int i=1;i<=maxs;++i)
{
int t=i;
while(t+i<=maxs)
{
t+=i;
cal[i]+=cal[t];
}
}
LL ans=0;
for(int i=maxs;i>=1;--i)
{
dp[i]=(quick_pow(2,cal[i],mod)-1+mod)%mod;
int t=i;
while(t+i<=maxs)
{
t+=i;
dp[i]=(dp[i]-dp[t]+mod)%mod;
}
ans=(ans+dp[i]*i)%mod;
}
printf("%lld\n",ans);
}
return 0;
}