HDU5656 所有子集GCD和

题目大意:给你一个集合,求所有子集的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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}


你可能感兴趣的:(数论)