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