题目大意:给你一个集合,求所有子集的gcd之和。所有数据均小于等于1000,集合数量小于等于1000.结果对1e8+7取模
思路:
我们考虑记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 <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define maxn 1005
#define MOD 100000007
#define mem(a , b) memset(a , b , sizeof(a))
#define LL long long
#define INF 100000000
int n;
int a[maxn];
int f[maxn];
int p[maxn];
int gcd(int a , int b)
{
if(b == 0) return a;
else return gcd(b , a % b);
}
int main()
{
int t , tmp , ans ;
cin >> t;
p[0] = 1;
for(int i = 1 ; i <= 1000 ; i ++) p[i] = (p[i-1] << 1) % MOD;
while(t--)
{
mem(f , 0);mem(a , 0);ans = 0;
cin >> n;
for(int i = 1 ; i <= n ; i ++) scanf("%d" , &tmp) , a[tmp]++;
for(int i = 1 ; i <= 1000 ; i ++)
{
int t = 0;
for(int j = i ; j <= 1000 ;j += i) t += a[j];
f[i] = p[t] - 1;
}
for(int i = 1000 ; i > 0 ; i --)
{
for(int j = i+i ; j <= 1000 ; j += i) f[i] = (f[i]-f[j] + MOD) % MOD;
ans = (ans + (LL)1*f[i] * i % MOD) % MOD;
}
cout << ans << endl;
}
return 0;
}