《算法竞赛进阶指南》0x37莫比乌斯函数 ZAP

题目链接:https://www.acwing.com/problem/content/217/

 

 

 通过容斥原理可以先算是1的倍数的所有数,然后将2,3,5,7倍数的数删掉,其中是2的倍数又是3的倍数的删了两次,所以要加回来,这时候可以发现,系数与莫比乌斯函数是对应的。只算是单个质因子的倍数的数,所以跟莫比乌斯函数有联系。求[a/x]*[b/x]时可以利用性质,将[1,min(a,b)]这段分成不超过O(sqrt(a)+sqrt(b))的小段,对每段求相应的莫比乌斯函数的和,所以可以预处理出莫比乌斯函数的前缀和。

代码:

 

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 50010;
int miu[maxn];
bool vis[maxn];
void get_miu(int n){//求莫比乌斯函数的前缀和 
    for(int i=1;i<=n;i++){
        miu[i]=1;    
        vis[i]=0;
    }
    for(int i=2;i<=n;i++){
        if(vis[i])continue;
        miu[i]=-1;//质数
        for(int j=2*i;j<=n;j+=i){
            vis[j]=1;
            if((j/i)%i==0)miu[j]=0;//分解质因数之后不是单个的 
            else miu[j]*=-1;//多增加了一个质因数项 
        } 
    }
    for(int i=1;i<=n;i++)miu[i]+=miu[i-1]; 
}
void Zap(){
    int a,b,k;
    scanf("%d%d%d",&a,&b,&k);
    a/=k;
    b/=k;
    int ans=0;
    if(a>b)swap(a,b);
    for(int x=1,gx;x<=a;x=gx+1){
        gx=min(a/(a/x),b/(b/x));
        ans+=(miu[gx]-miu[x-1])*(a/x)*(b/x);
    }
    printf("%d\n",ans);
}
int main(){
    get_miu(50005);
    int n;
    cin>>n;
    while(n--)Zap();
    return 0;
}

 

posted @ 2020-07-10 22:04  WA自动机~  阅读(291)  评论(0编辑  收藏  举报