链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3994
这道题目学到的
数学是硬伤….OI早晚要受到牵连,这不,叫这道题卡了一晚上。
有些方法经常要用到
一、枚举次序的变化,如果要先枚举一个数字
x
,再枚举其倍数
二、
∑ni=1∑Mj=1A[gcd(i,j)=1]
其中A是随便一个式子,就等价于
∑ni=1∑Mj=1A∑d|gcd(i,j)μ(d)
三、
⌊nid⌋=⌊⌊nd⌋i⌋
四、
∑i∑jf(i)g(j)=∑if(i)∑jg(j)
,就是说
∑
后面和枚举变量没关的东西统统可以拿出来。
题解
一个结论,
∑i=1N∑j=1Md(ij)=∑i−1N∑j=1M⌊Ni⌋⌊Mj⌋[gcd(i,j)=1]
应用第二条
=∑i=1N∑j=1M⌊Ni⌋⌊Mj⌋∑d|gcd(i,j)μ(d)
应用第一、四条
=∑d=1min(N,M)μ(d)∑d|iN⌊Ni⌋∑d|jM⌊Mj⌋
继续
=∑d=1min(N,M)μ(d)∑i=1⌊Nd⌋⌊Nid⌋∑j=1⌊Md⌋⌊Mjd⌋
应用第三条
=∑d=1min(N,M)μ(d)∑i=1⌊Nd⌋⌊⌊Nd⌋i⌋∑j=1⌊Md⌋⌊⌊Md⌋j⌋
可以发现,后面两个 ∑ 的值都只和上界有关,因此可以预处理 f[x]=∑xi=1⌊xi⌋
最终的答案为
∑d=1min(n,m)μ(d)f(⌊Nd⌋)f(⌊Md⌋)
这个可以 N−−√ 做
f(x) 的预处理,我最开始是用 NN−−√ 处理的,后来听说这个东西就是约数个数和,于是改用了 NlogN 的方法。
代码在bzoj是AC的,但是洛谷上会TLE(我怎么知道是什么情况??)。
代码
//莫比乌斯反演
#include <cstdio>
#include <algorithm>
#define ll long long
#define maxn 50000
using namespace std;
ll mu[maxn+10], prime[maxn+10], f[maxn+10], mark[maxn+10], s[maxn+10];
void init()
{
ll i, j;
s[1]=mu[1]=1;
for(i=2;i<=maxn;i++)
{
if(!mark[i])prime[++prime[0]]=i,mu[i]=-1;
for(j=1;j<=prime[0] and i*prime[j]<=maxn;j++)
{
mark[i*prime[j]]=1;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
s[i]=s[i-1]+mu[i];
}
for(i=1;i<=maxn;i++)
for(j=i;j<=maxn;j+=i)f[j]++;
for(i=1;i<=maxn;i++)f[i]+=f[i-1];
}
ll calc(ll n, ll m)
{
ll ans=0, i, last;
if(n>m)swap(n,m);
for(i=1;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(s[last]-s[i-1])*f[n/i]*f[m/i];
}
return ans;
}
int main()
{
ll T, N, M;
init();
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld",&N,&M);
printf("%lld\n",calc(N,M));
}
return 0;
}