hdu4746 Mophues 莫比乌斯

/**
题目:hdu4746 Mophues
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4746
题意:求x,y在给定范围内gcd(x,y)分解素因子的个数<=p的对数。
 (n, m, P <= 5×105. Q <=5000).
思路:

f(n)表示给定范围内gcd(x,y)==n的对数。
g(n)表示给定范围内gcd(x,y)为n的倍数的对数。

f(n) = sigma[n|d]mu[d/n]*g(d) = sigma[n|d]mu[d/n]*(n/d)*(m/d) ;

ans = sigma[1<=x<=min(n,m)]sigma[x|d]mu[d/x]*g(d)

    = sigma[1<=d<=min(n,m)](g(d)*sigma[x是d的约数]mu[d/x]);

    sigma[x是d的约数]mu[d/x] 是 g(d)的系数。系数可以用前缀和预处理。

    但是本题要求的是gcd(x,y)的分解质因子个数<=p; 所以sigma[x是d的约数]mu[d/x]这里x不仅是d的约数,且要满足x的分解质因子个数<=p (ps:这里的x就是f(n)的n);

    定义sum[d]表示 sigma[x是d的约数]mu[d/x], 那么sum[d][num]表示 sigma[x是d的约数,x的分解质因子个数为num]mu[d/x];

    然后处理sum[d][num]表示sum[d][0]~sum[d][num]的前缀和。

    然后处理sum[d][num]表示sum[0][num]~sum[d][num]的前缀和。

    那么就可以用除法的取值sqrt(N)级别快速计算。




*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
typedef long long LL;
#define ms(x,y) memset(x,y,sizeof x)
typedef pair<int, int> P;
const LL INF = 1e10;
const int mod = 1e9 + 7;
const int maxn = 5e5 + 100;
int prime[maxn], tot, not_prime[maxn];
int mu[maxn], sum[maxn][20], cnt[maxn];
void mobius()
{
    mu[1] = 1;
    tot = 0;
    for(int i = 2; i < maxn; i++){
        if(!not_prime[i]){
            mu[i] = -1;
            prime[++tot] = i;
            cnt[i] = 1;
        }
        for(int j = 1; prime[j]*i<maxn; j++){
            not_prime[prime[j]*i] = 1;
            cnt[prime[j]*i] = cnt[i]+1;///cnt[i]表示i这个数分解素因子的个数。
            if(i%prime[j]==0){
                mu[prime[j]*i] = 0;
                break;
            }
            mu[prime[j]*i] = -mu[i];
        }
    }

    for(int i = 1; i < maxn; i++){
        for(int j = i; j < maxn; j+=i){
            sum[j][cnt[i]] += mu[j/i];
        }
    }

    for(int i = 1; i < maxn; i++){
        for(int j = 1; j < 20; j++){
            sum[i][j] += sum[i][j-1];
        }
    }

    for(int j = 0; j < 20; j++){
        for(int i = 1; i< maxn; i++){
            sum[i][j]  += sum[i-1][j];
        }
    }

}
LL solve(int n,int m,int p)
{
    if(n>m) swap(n,m);
    LL ans = 0;
    int last;
    for(int i = 1; i <= n; i = last+1){
        last = min(n/(n/i),m/(m/i));
        ans += (LL)(sum[last][p]-sum[i-1][p])*(n/i)*(m/i);
    }
    return ans;
}
int main()
{
    //freopen("YYnoGCD.in","r",stdin);
    //freopen("YYnoGCD.out","w",stdout);
    //freopen("in.txt","r",stdin);
    int n, m, p;
    int T;
    mobius();
    cin>>T;
    while(T--){
        scanf("%d%d%d",&n,&m,&p);
        if(p>19){
            printf("%lld
",(LL)n*m); continue;
        }
        printf("%lld
",solve(n,m,p));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiaochaoqun/p/7375644.html