CDOJ 1171 两句话题意

题目链接:http://acm.uestc.edu.cn/#/problem/show/1171

题解:

这道题应该从gcd出来的值入手。

我们要求所有子集的gcd的和
首先我们先统计一下每个数字出现的次数。
然后从大到小找,每次都可以算出来gcd是当前值的子集数量。
我们要这么做:假设当前算gcd=i的情况,我们把所有i的倍数的数统计一下,那么在这些数(假设有n个)里,我们只要选至少一个就可以得到gcd=(i的倍数)的情况总数(2^n-1)。但是这不是我们需要的i,需要减去大于i的且是i的倍数的情况数,幸运的是,我们是逆序算的,所以所有i的倍数的情况数都已经算过了。只要减去就可以了^_^,于是可以得到每个gcd的值的情况数,把它乘上对应gcd的k次方(这里需要快速幂)再求和就可以了。

代码:

#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<cstdio>
#define MAX_N 2000006
using namespace std;

typedef long long ll;

int a[MAX_N];
int n;

int T;
const int mod=10000007;

int cnt[MAX_N];

ll Pow(ll a,ll b) {
    ll res = 1;
    while (b) {
        if (b & 1)res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

ll ways[MAX_N];
int k;

int main() {
    scanf("%d", &T);
    while (T--) {
        int maxA = -1;
        memset(a, 0, sizeof(a));
        memset(cnt, 0, sizeof(cnt));
        memset(ways, 0, sizeof(ways));
        scanf("%d%d", &n, &k);
        for (int i = 0; i < n; i++) {
            int t;
            scanf("%d", &t);
            maxA = max(maxA, t);
            a[t]++;
        }
        for (int i = 1; i <= maxA; i++)
            for (int j = 1; j * i <= maxA; j++)
                cnt[i] += a[i * j];
        for (int i = 1; i <= maxA; i++)
            ways[i] = (Pow(2, cnt[i]) - 1 + mod) % mod;
        for (int i = maxA; i >= 1; i--)
            for (int j = 2; j * i <= maxA; j++)
                ways[i] = (ways[i] - ways[i * j] + mod) % mod;
        ll ans = 0;
        for (int i = 1; i <= maxA; i++)
            ans = (ans + ways[i] * Pow(i, k)) % mod;
        printf("%lld", ans);
        if(T!=0)printf("\n");
    }
    return 0;
}

 

posted @ 2015-09-25 00:19  好地方bug  阅读(310)  评论(0编辑  收藏  举报