首页 > 代码库 > hdu6053(莫比乌斯+容斥+分块)
hdu6053(莫比乌斯+容斥+分块)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6053
题意: 给出一个含 n 个元素的 a 数组, 求 bi <= ai 且 gcd(b1, ..., bn) >= 2 的 b 数组的数目;
思路: 首先想到的方法是枚举 gcd, 对于每个 gcd x 的情况, 将所有 bi / x 连乘, 然后将所有 gcd 的情况累加一下就能的到答案了 .
然而其时间复杂度为 O(t * min(a) * n), 铁定 tle;
对于后面连乘部分是可以优化一下的 . 把 a 数组元素装到一个桶里面, 然后枚举 gcd 时按照 ai / gcd 的值分下块,
对于区间 [gcd * k, gcd * (k + 1 ) - 1], 显然区间内的值除 gcd 的商都为 k, 若 a 中有 cnt 个元素处于该区间, 那么该块内的 b 数组元素有 k^cnt 种选择方案,
再将不同块的方案数乘起来就是当前 gcd 对答案的贡献了 .
然而上面还会出现重复计算(个人感觉这个有点难理解):
对于不能分解成不同素数乘积形式的 gcd, 在第一次计算其中多次出现的素因子作 gcd 时已经计算过了, 所以不累加到答案上 .
对于能分解成不同素数乘积形式的 gcd, 其中重复计算的我们可以用容斥去重, 奇加偶减 .
可以发现这和 moblus 函数很相近, 对与不能分解成不同素数乘积形式的数, 其 mu 值为 0, 对于能分解成不同素数乘积形式的数, 若其长度为奇数, mu 值为 -1, 长度为偶数则 mu 值为 1, 恰好与容斥的奇加偶减相反, 所以给 mu 加个负号即可 .
代码:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define ll long long 5 using namespace std; 6 7 const int mod = 1e9 + 7; 8 const int MAXN = 1e5 + 10; 9 10 bool check[MAXN]; 11 int prime[MAXN], mu[MAXN], vis[MAXN << 1]; 12 13 void Moblus(void){ 14 memset(check, false, sizeof(check)); 15 int tot = 0; 16 mu[1] = 1; 17 for(int i = 2; i < MAXN; i++){ 18 if(!check[i]){ 19 prime[tot++] = i; 20 mu[i] = -1; 21 } 22 for(int j = 0; j < tot && i * prime[j] < MAXN; j++){ 23 check[i * prime[j]] = true; 24 if(i % prime[j] == 0){ 25 mu[i * prime[j]] = 0; 26 break; 27 }else mu[i * prime[j]] = -mu[i]; 28 } 29 } 30 } 31 32 ll get_pow(ll x, int n){ 33 ll ans = 1; 34 while(n){ 35 if(n & 1) ans = ans * x % mod; 36 x = x * x % mod; 37 n >>= 1; 38 } 39 return ans; 40 } 41 42 int main(void){ 43 Moblus(); 44 int t, n, x; 45 scanf("%d", &t); 46 for(int cas = 1; cas <= t; cas++){ 47 memset(vis, 0, sizeof(vis)); 48 int mi = MAXN, mx = - MAXN; 49 scanf("%d", &n); 50 for(int i = 0; i < n; i++){ 51 scanf("%d", &x); 52 vis[x] += 1; 53 mi = min(mi, x); 54 mx = max(mx, x); 55 } 56 for(int i = 1; i < (MAXN << 1); i++){//注意范围 57 vis[i] += vis[i - 1]; 58 } 59 ll ans = 0; 60 for(int i = 2; i <= mi; i++){ 61 ll cnt = 1; 62 if(mu[i]){ 63 for(int j = i; j <= mx; j += i){ 64 cnt = (cnt * get_pow((ll)(j / i), vis[j + i - 1] - vis[j - 1])) % mod; 65 } 66 } 67 ans = (ans - cnt * mu[i] + mod) % mod; 68 } 69 printf("Case #%d: %lld\n", cas, ans); 70 } 71 return 0; 72 }
hdu6053(莫比乌斯+容斥+分块)