BZOJ 2820 莫比乌斯反演

思路:

$\Sigma_{i=1}^n\Sigma_{j=1}^mgcd(i,j)==p(p是素数)$


$\Sigma_{p是素数}^{p<=n}\Sigma_{i=1}^{\lfloor \frac{n}{p} \rfloor}\Sigma_{j=1}^{\lfloor \frac{m}{p} \rfloor}gcd(i,j)==1$


由$e=μ|1$可得$gcd(i,j)==1 等价于 \Sigma_{d|gcd(i,j)} μ(d)$


所以原式为


$\Sigma_{p是素数}^{p<=n}\Sigma_{i=1}^{\lfloor \frac{n}{p} \rfloor}\Sigma_{j=1}^{\lfloor \frac{m}{p}\rfloor}\Sigma_{d|gcd(i,j)}μ(d)$


$\Sigma_{p是素数}^{p<=n}\Sigma_{i=1}^{\lfloor \frac{n}{p} \rfloor}\Sigma_{j=1}^{\lfloor \frac{m}{p}\rfloor}\Sigma_{d|i且d|j}μ(d)$


$\Sigma_{p是素数}^{p<=n}\Sigma_{d=1}^{\frac{n}{p}}\lfloor{\frac{n}{pd}}\rfloor\lfloor{\frac{m}{pd}}\rfloor μ(d)$


设$k=pd$


$\Sigma_{k=1}^{min(n,m)}\lfloor{\frac{n}{k}}\rfloor\lfloor{\frac{m}{k}}\rfloor\Sigma_{p|k}μ(\frac{k}{p})$

 

设$g(x)=\Sigma_{p|k}μ(\frac{k}{p})$


可以预处理g(x)


怎么求g(x)呢


有两种方法


1.暴力枚举


枚举素数 p


用$μ(p)$更新$μ(i*p)(i*p<=max(n,m))$


这样素数有$n/logn$个


枚举i的均摊复杂度是$O(logn)$的


乘起来就是$O(n)$了


2.
线性筛

对于素数p g(p)=1(显然)

 

由于$μ(x)_{x的因子有平方项}=0$


所以$prime[j]|i$的时候$g(i*prime[j])$的结果就是$μ(i)$了


因为我们求的是$\Sigma_{p|k}μ(\frac{k}{p})$而且$μ(x)$是积性函数


那我们不妨设$i*prime[j]=k$


设$P$是包含$prime[j]$的所有素数 $p$是不包含$prime[j]$的所有素数


$\Sigma_{P|k}μ(\frac{k}{P})=\Sigma_{P|k}μ(\frac{i*prime[j]}{P})$


$=\Sigma(μ(\frac{i}{p})*μ(prime[j]))+μ\frac{(i*prime[j])}{prime[j]}$


$=μ(prime[j])*g[i]+μ(i)=-g[i]+μ(i)$

 

处理完了g(x)


原式就是这个样子的$\Sigma_{k=1}^{min(n,m)}\lfloor{\frac{n}{k}}\rfloor\lfloor{\frac{m}{k}}\rfloor g(k)$


对于g(k)搞个前缀和


$\lfloor{\frac{n}{k}}\rfloor\lfloor{\frac{m}{k}}\rfloor$这个东西可以分块

 


终于搞完了
喜闻乐见
吼吼

枚举素数的

//By SiriusRen
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=10000005;
#define int long long
int cases,n,m,mu[maxn],p[maxn],sum[maxn],f[maxn],tot;
long long ans;bool vis[maxn];
signed main(){
    mu[1]=1;
    for(int i=2;i<maxn;i++){
        if(!vis[i])p[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot&&i*p[j]<maxn;j++){
            mu[i*p[j]]=-mu[i],vis[i*p[j]]=1;
            if(!(i%p[j])){mu[i*p[j]]=0;break;}
        }
    }
    for(int i=1;i<=tot;i++)
        for(int j=1;j*p[i]<maxn;j++)f[j*p[i]]+=mu[j];
    for(int i=1;i<maxn;i++)f[i]+=f[i-1];
    scanf("%d",&cases);
    while(cases--){
        scanf("%d%d",&n,&m),ans=0;
        if(n>m)swap(n,m);
        for(int i=1,j;i<=n;i=j+1){
            j=min(n/(n/i),m/(m/i));
            ans+=(f[j]-f[i-1])*(n/i)*(m/i);
        }
        printf("%lld\n",ans);
    }
}

这是线性筛的

//By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=10000005;
int T,prime[N],vis[N],mu[N],tot,n,m;ll f[N],ans;
int main(){
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!vis[i])prime[++tot]=i,mu[i]=-1,f[i]=1;
        for(int j=1;i*prime[j]<N&&j<=tot;j++){
            vis[i*prime[j]]=1,mu[i*prime[j]]=-mu[i],f[i*prime[j]]=-f[i]+mu[i]; 
            if(i%prime[j]==0){mu[i*prime[j]]=0;f[i*prime[j]]=mu[i];break;}
        }f[i]+=f[i-1];
    }
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m),ans=0;
        for(int l=1,r;l<=min(n,m);l=r+1){
            r=min(n/(n/l),m/(m/l));
            ans+=1ll*(f[r]-f[l-1])*(n/l)*(m/l);
        }printf("%lld\n",ans);
    }
}

 

posted @ 2017-04-07 23:32  SiriusRen  阅读(165)  评论(0编辑  收藏  举报