HDU 4746 Mophues (莫比烏斯反演應用)

_TCgogogo_發表於2015-08-21


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\n", p > 18 ? (ll) n * m : cal(n, m));
    }
}


相關文章