zoukankan      html  css  js  c++  java
  • 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
    ", cas, ans);
    70     }
    71     return 0;
    72 }
    View Code
  • 相关阅读:
    【python小随笔】单例模式设计(易懂版)
    【python小随笔】函数的初始化与私有化
    【python小随笔】List列表的常见函数与切片
    【itsdangerous】的加密解密原理(易懂版)
    【Django入坑之路】Django后台上传图片,以及前端的显示
    【插拔式】分页+bootstrap4(开源)
    luogu P1231 教辅的组成 |网络流最大匹配
    luogu P3376 【模板】网络最大流 |Dinic
    luogu P4177 [CEOI2008]order |最大权闭合子图
    luogu P4562 [JXOI2018]游戏 |组合数学
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7252378.html
Copyright © 2011-2022 走看看