zoukankan      html  css  js  c++  java
  • HDU 4746 Mophues (莫比乌斯反演应用)


    Mophues

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 327670/327670 K (Java/Others)

    Total Submission(s): 980    Accepted Submission(s): 376

    Problem Description

    As we know, any positive integer C ( C >= 2 ) can be written as the multiply of some prime numbers:
        C = p1×p2× p3× ... × pk
    which p1, p2 ... pk are all prime numbers.For example, if C = 24, then:
        24 = 2 × 2 × 2 × 3
        here, p1 = p2 = p3 = 2, p4 = 3, k = 4
    Given two integers P and C. if k<=P( k is the number of C's prime factors), we call C a lucky number of P.
    Now, XXX needs to count the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of a given P ( "gcd" means "greatest common divisor").
    Please note that we define 1 as lucky number of any non-negative integers because 1 has no prime factor.
     
    Input
    The first line of input is an integer Q meaning that there are Q test cases.
    Then Q lines follow, each line is a test case and each test case contains three non-negative numbers: n, m and P (n, m, P <= 5×105. Q <=5000).
     
    Output
    For each test case, print the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of P.
     
    Sample Input
    2 10 10 0 10 10 1
     
    Sample Output
    63 93
     
    Source
     
    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4746

    题目大意:定义num[i]为将i唯一分解后全部质因子的个数。要求num[gcd(a, b)] <= p的(a,b)的对数,当中1 <= a <= n。1 <= b <= m

    题目分析:这题T了一天,先不考虑1,题目给的p的最大值是5e5,因此num[i]最大为18,由于2^19就大于5e5了
    定义f(d)为gcd(a,b) = d的个数,g(d)为gcd(a,b) = d的倍数的个数,显然g(d)非常好求。就是(n / d) * (m / d)
    又g(d) = f(d) + f(2d) + f(3d) + ...对此式进行莫比乌斯反演得到
    f(d) = u(1)g(d) + u(2)g(2d) + u(3)g(3d) + ...。最后答案为Σu(k)g(kd),此时若直接枚举d,就算计算f(d)用分块求和优化成接近sqrt(n)。n*sqrt(n)接近2e8肯定超时,因此要换别的思路,考虑到num[i]最大仅仅有18。我们能够预处理出以i为最大公约数。且分解i后质因子个数等于num[i]的方案数。依据公式有sum[ki][num[i]] += u[k]。令j=ki,则sum[j][num[i]] += u[j / i],注意这里算的仅仅是莫比乌斯函数的贡献值,
    举个样例。比方
    f(2) = u(1)g(2) + u(2)g(4) + u(3)g(6) + ...
    f(3) = u(1)g(3) + u(2)g(6) + u(3)g(9) + ...
    答案肯定要把它们加起来,注意到g(6)出现了两次,能够理解为6这个数字对num[i] = 1的情况有两次贡献, 因此能够写成(u(2) + u(3)) * g(6)
    然后再处理分解i后质因子个数小于等于num[i]的方案数,最后再处理以i为最大公约数的前缀和,预处理工作就结束了,复杂度为nlogn。在线计算时用分块求和优化,复杂度为qsqrt(n)。总的复杂度大概为nlogn+qsqrt(n)


    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    using namespace std;
    int const MAX = 5e5 + 5;
    int n, m, p, pnum;
    int mob[MAX], pr[MAX], sum[MAX][20];
    int num[MAX];
    bool prime[MAX];
    
    void Mobius()
    {
        pnum = 0;
        memset(prime, true, sizeof(prime));
        mob[1] = 1;
        for(int i = 2; i < MAX; i++)
        {
            if(prime[i])
            {
                pr[pnum ++] = i;
                num[i] = 1;
                mob[i] = -1;
            }
            for(int j = 0; j < pnum && i * pr[j] < MAX; j++)
            {
                num[i * pr[j]] = num[i] + 1;
                prime[i * pr[j]] = false;
                if(i % pr[j] == 0)
                {
                    mob[i * pr[j]] = 0;
                    break;
                }
                mob[i * pr[j]] = -mob[i];
            }
        }
    }
    
    void Init()
    {
        Mobius();
        for(int i = 1; i < MAX; i++)
            for(int j = i; j < MAX; j += i)
                sum[j][num[i]] += mob[j / i];
        for(int i = 1; i < MAX; i++)
            for(int j = 1; j < 19; j++)
                sum[i][j] += sum[i][j - 1];
        for(int i = 1; i < MAX; i++)
            for(int j = 0; j < 19; j++)
                sum[i][j] += sum[i - 1][j];
    }
    
    ll cal(int l, int r)
    {
        ll ans = 0;
        if(l > r)
            swap(l, r);
        for(int i = 1, last = 0; i <= l; i = last + 1)
        {
            last = min(l / (l / i), r / (r / i));
            ans += (ll) (l / i) * (r / i) * (sum[last][p] - sum[i - 1][p]);  
        }
        return ans;
    }   
    
    int main()
    {
        Init();
        int T;
        scanf("%d", &T);
        while(T --)
        {
            scanf("%d %d %d", &n, &m, &p);
            printf("%lld
    ", p > 18 ? (ll) n * m : cal(n, m));
        }
    }
    


  • 相关阅读:
    multiprocessing 源码解析 更新中......
    loadrunner 更新中......
    Java IO
    echarts水球图小数点不显示问题+组件默认值
    双柱表格组件
    表格生成后修改echarts图表样式
    vue中引入单张图片+两张壁纸手动切换
    配置全局组件
    vue使用babel-plugin-import按需引入Ant Design View + babel-plugin-component按需引入element-ui
    vue深浅拷贝的思索
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7387785.html
Copyright © 2011-2022 走看看