HDU 4746 Mophues(莫比乌斯反演)

题意:求\(1\leq i \leq N,1\leq j \leq M,gcd(i,j)\)的质因子个于等于p的对数。
分析:加上了对质因子个数的限制。
\(f(d):[gcd(i,j)=d]\) , \(F(d):[d|gcd(i,j)]\) ,k是满足质因子<=p的数。
\(ans = \sum_{k}f(k) = \sum_{k}\sum_{k|d}u(\frac{d}{k})\lfloor \frac{N}{d}\rfloor \lfloor\frac{M}{d}\rfloor = \sum_{d}\lfloor \frac{N}{d}\rfloor \lfloor\frac{M}{d}\rfloor\sum_{k|d}u(\frac{d}{k})\)
\(\sum_{k|d}u(\frac{d}{k})\)可以预处理得到。
由于限制了质因子的个数,并且我们还想用分块的方法求出最后答案,于是设\(sum(a,b):\sum_{i=1}^{a}\sum_{k|i且k质因子个数\leq j}u(\frac{i}{k})\)。在打出莫比乌斯函数表后,可以在预处理出\(sum(a,b)表\),由于1e6以内的数质因子个数不会超过20,因此第二维开到20即可。
那么计算结果时可以分块加速求出ans的值。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=5e5+5;
bool vis[maxn];
int prime[maxn],mu[maxn];
int sum[maxn][20];
int pricnt[maxn];
void init(){
    memset(vis,false,sizeof(vis));
    mu[1] = 1;
    prime[0] = 0;
    int cnt=0;
    for(int i=2;i<maxn;++i){
        if(!vis[i]){
            mu[i] = -1;
            pricnt[i] = 1;
            prime[++cnt] = i;
        }
        for(int j=1;j<=cnt;++j){
            if(i*prime[j] >= maxn)  break;
            pricnt[i*prime[j]] = pricnt[i]+1;
            vis[i*prime[j]] = true;
            if(i % prime[j]){
                mu[i*prime[j]] = -mu[i];
            }
            else{
                mu[i*prime[j]] = 0;
                break;
            }
        }
    }
}

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

LL gao(LL n,LL m,LL p)      //枚举p
{
    LL ans = 0;
    for(int i=1,j;i<=n;i=j+1){
        j = min(n/(n/i),m/(m/i));
        ans += (sum[j][p]-sum[i-1][p])*(n/i)*(m/i);
    }
    return ans; 
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    init();
    prepare();
    LL N,M,p;
    int T; scanf("%d",&T);
    while(T--){
        scanf("%lld %lld %lld",&N, &M,&p);
        LL res=0;
        if(p>=20){
            res = N*M;
        }
        else{
            if(N>M) swap(N,M);
            res = gao(N,M,p);
        }
        printf("%lld\n",res);
    } 
    return 0;
}
posted @ 2018-08-27 15:52  xiuwenL  阅读(156)  评论(0编辑  收藏  举报