BZOJ 2820 YY的GCD ——莫比乌斯反演

我们可以枚举每一个质数,那么答案就是

$\sum_{p}\sum_{d<=n}\mu(d)*\lfloor n / pd \rfloor *\lfloor m / pd \rfloor$

直接做?TLE

考虑优化,由于看到了pd是成对出现的,令T=pd

$ans=\sum_{T<=min(n,m)}\lfloor n / T \rfloor *\lfloor m / T \rfloor \sum_{p \mid T}\mu(T/p)$

或者

$ans=\sum_{T<=min(n,m)}\lfloor n / T \rfloor *\lfloor m / T \rfloor \sum_{d \mid T}\mu(d)$

显然第一个更好求,我们只需要枚举质数即可

根据欧拉公式近似$\sum_{i=1} \frac{1}{i} = ln n + r$

每个质数均摊logn的复杂度,那么质数个数是n/logn的,我们就可以O(n)预处理了。

如果枚举第二个的话,复杂度是nlogn的

然后算出前缀和,进行下界函数分块即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define inf 0x3f3f3f3f
#define maxn 10000005
int mu[maxn],pr[maxn],top,sim[maxn];
bool vis[maxn];
void init()
{
    memset(vis,false,sizeof vis);
    mu[1]=1;
    F(i,2,maxn-1)
    {
        if (!vis[i]) pr[++top]=i,mu[i]=-1;
        F(j,1,top)
        {
            if (pr[j]*i>=maxn) break;
            vis[i*pr[j]]=true;
            if (i%pr[j]==0) {mu[i*pr[j]]=0;break;}
            mu[i*pr[j]]=-mu[i];
        }
    }
//  F(i,1,10) printf("%d ",mu[i]);
}
 
int t,n,m;
 
ll solve(int n,int m)
{
    ll ret=0;
    if (n>m) swap(n,m);
    for (int i=1,last=0;i<=n;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ret+=((ll)sim[last]-sim[i-1])*(m/i)*(n/i);
    }
    return ret;
}
 
int main()
{
    init();
    F(i,1,top)
        F(j,1,inf)
        {
            if (pr[i]*j>=maxn) break;
            sim[pr[i]*j]+=mu[j];
        }
    F(i,1,maxn-1) sim[i]+=sim[i-1];
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&m);
        printf("%lld\n",solve(n,m));
    }
}

然后我们发现这个函数是可以线性筛的,尽管它不是积性函数

$g(pr[j]*i)=\mu (i) ,pr[j] \mid i$

$g(pr[j]*i)=\mu(i)-g[i] , pr[j] \nmid i$

然后就可以$\Theta (n)$去预处理了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define inf 0x3f3f3f3f
#define maxn 10000005
int mu[maxn],pr[maxn],top,sim[maxn];
bool vis[maxn];
void init(int tmp)
{
    memset(vis,false,sizeof vis);
    mu[1]=1;sim[1]=0;
    F(i,2,tmp)
    {
        if (!vis[i])
		{
			pr[++top]=i;
			mu[i]=-1;
			sim[i]=1;
		}
        F(j,1,top)
        {
            if (pr[j]*i>tmp) break;
            vis[i*pr[j]]=true;
            if (i%pr[j]==0)
			{
				mu[i*pr[j]]=0;
				sim[i*pr[j]]=mu[i];
				break;
			}
            mu[i*pr[j]]=-mu[i];
            sim[i*pr[j]]=mu[i]-sim[i];
        }
    }
    F(i,1,tmp) sim[i]+=sim[i-1];
}
  
int t;
  
ll solve(int n,int m)
{
    ll ret=0;
    for (int i=1,last=0;i<=n;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ret+=((ll)sim[last]-sim[i-1])*(m/i)*(n/i);
    }
    return ret;
}
  
int n[10005],m[10005];

int main()
{
    F(i,1,maxn-1) sim[i]+=sim[i-1];
    scanf("%d",&t);int tmp=0;
    F(i,1,t)
    {
    	scanf("%d%d",&n[i],&m[i]);
    	if (n[i]>m[i]) swap(n[i],m[i]);
    	tmp=max(tmp,n[i]);
	}
	init(tmp);
	F(i,1,t)printf("%lld\n",solve(n[i],m[i]));
}

  

posted @ 2017-03-21 11:37  SfailSth  阅读(214)  评论(0编辑  收藏  举报