BZOJ 1101: [POI2007]Zap(莫比乌斯反演)

本文详细介绍了如何解决一个复杂的数学算法问题,即求解两个数列的GCD(最大公约数)的双重求和,并通过μ函数(Mobius函数)进行转换和优化,最终利用前缀和与整除分块的方法实现高效计算。

传送门

解题思路

  \[ \sum\limits_{i=1}^n\sum\limits_{j=1}^mgcd(i,j)=k \]

\[ \sum\limits_{i=1}^{\frac{n}{m}}\sum\limits_{j=1}^{\frac{m}{k}}gcd(i,j)=1 \]

\[ \sum\limits_{i=1}^{\frac{n}{k}}\sum\limits_{j=1}^{\frac{m}{k}}\sum\limits_{d|n,d|m}\mu(d) \]

\[ \sum\limits_{i=1}^{n}\mu(d)\sum\limits_{i=1}^{\frac{n}{kd}}\sum\limits_{j=1}^{\frac{m}{kd}} \]

  然后这样就做完了,\(\mu\)搞一个前缀和,其余的整除分块

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int MAXN = 50005;
typedef long long LL;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;
}

int n,miu[MAXN]={0,1},prime[MAXN],cnt,ans;
bool vis[MAXN];

int main(){
    for(int i=2;i<=50000;i++){
        if(!vis[i]) {prime[++cnt]=i;miu[i]=-1;}
        for(int j=1;j<=cnt && prime[j]*i<=50000;j++){
            vis[i*prime[j]]=1;
            if(!(i%prime[j])) {miu[i*prime[j]]=0;break;}
            miu[i*prime[j]]=-miu[i];
        }
    }
    for(int i=1;i<=50000;i++) miu[i]+=miu[i-1];
    n=rd();int a,b,d;
    while(n--){
        a=rd(),b=rd(),d=rd();if(a>b) swap(a,b);
        for(int l=1,r;l<=a/d;l=r+1){
            r=min((a/d)/(a/d/l),(b/d)/(b/l/d));
            ans+=(miu[r]-miu[l-1])*(a/l/d)*(b/l/d);
        }
        printf("%d\n",ans);ans=0;
    }
    return 0;
}

转载于:https://www.cnblogs.com/sdfzsyq/p/10261946.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值