【BZOJ3994】约数个数和（SDOI2015）-莫比乌斯反演+数论分块

$d\left(ij\right)=\sum _{x|i}\sum _{y|j}\left[gcd\left(x,y\right)=1\right]$

$ans=\sum _{i=1}^{n}\sum _{j=1}^{m}\sum _{x|i}\sum _{y|j}\left[gcd\left(x,y\right)=1\right]$
$x,y$换出来，得到：
$ans=\sum _{x=1}^{n}\sum _{y=1}^{m}\left[gcd\left(x,y\right)=1\right]⌊\frac{n}{x}⌋⌊\frac{m}{y}⌋$

$ans=\sum _{x=1}^{n}⌊\frac{n}{x}⌋\sum _{y=1}^{m}⌊\frac{m}{y}⌋\sum _{d|gcd\left(x,y\right)}\mu \left(d\right)$
$d$换出来，得到：
$ans=\sum _{d=1}^{min\left(n,m\right)}\mu \left(d\right)\sum _{x=1}^{⌊\frac{n}{d}⌋}⌊\frac{n}{xd}⌋\sum _{y=1}^{⌊\frac{m}{d}⌋}⌊\frac{m}{yd}⌋$

$⌊\frac{n}{xy}⌋=k$，则有$n=k\left(xy\right)+r$，其中$0\le r，又令$r={r}_{0}x+{r}_{1}$，其中$0\le {r}_{1}，可以知道$0\le {r}_{0}，那么有$⌊\frac{n}{x}⌋=ky+{r}_{0}$，所以$⌊\frac{⌊\frac{n}{x}⌋}{y}⌋=k$

$ans=\sum _{d=1}^{min\left(n,m\right)}\mu \left(d\right)\sum _{x=1}^{⌊\frac{n}{d}⌋}⌊\frac{⌊\frac{n}{d}⌋}{x}⌋\sum _{y=1}^{⌊\frac{m}{d}⌋}⌊\frac{⌊\frac{m}{d}⌋}{y}⌋$

$ans=\sum _{d=1}^{min\left(n,m\right)}\mu \left(d\right)S\left(⌊\frac{n}{d}⌋\right)S\left(⌊\frac{m}{d}⌋\right)$

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T;
ll n[50010],m[50010],maxn=0;
ll prime[50010],mu[50010],summu[50010];
ll id[50010],d[50010],sumd[50010];
bool vis[50010]={0};

void calc(ll limit)
{
mu[1]=d[1]=1;
prime[0]=0;
for(ll i=2;i<=limit;i++)
{
if (!vis[i])
{
prime[++prime[0]]=i;
mu[i]=-1;
id[i]=2;
d[i]=2;
}
for(ll j=1;j<=prime[0]&&i*prime[j]<=limit;j++)
{
vis[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
id[i*prime[j]]=id[i]+1;
d[i*prime[j]]=d[i]*(id[i]+1ll)/id[i];
break;
}
mu[i*prime[j]]=-mu[i];
id[i*prime[j]]=2;
d[i*prime[j]]=d[i]*id[i*prime[j]];
}
}
summu[0]=sumd[0]=0;
for(ll i=1;i<=limit;i++)
{
summu[i]=summu[i-1]+mu[i];
sumd[i]=sumd[i-1]+d[i];
}
}

ll solve(ll n,ll m)
{
ll ans=0;
for(ll i=min(n,m);i>=1;i=max(n/(n/i+1),m/(m/i+1)))
{
ll l=max(n/(n/i+1),m/(m/i+1))+1,r=i;
ans+=(summu[r]-summu[l-1])*sumd[n/i]*sumd[m/i];
}
return ans;
}

int main()
{
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%lld%lld",&n[i],&m[i]);
maxn=max(maxn,max(n[i],m[i]));
}

calc(maxn);
for(int i=1;i<=T;i++)
printf("%lld\n",solve(n[i],m[i]));

return 0;
}
posted @ 2018-05-04 12:39  Maxwei_wzj  阅读(77)  评论(0编辑  收藏  举报