链接
思路
上次做这题的时候用了一些玄学水过去了,这次我写了个有理有据的
算法,
假设n < m,根据题意写出
令
这种情况下直接做是 的
因为每次查询 和 都在变,因此无法预处理式子后面的那一段,考虑如何把 和 从后面拿出来,然后再通过预处理一些和 , 无关的部分来降低每次询问的计算次数
令
这里就看出来了,只要预处理后面那一坨 ,每次就可以 了
这题最麻烦的就在这里,这个东西要分好几层来看,首先令 ,那么上面那一坨就等于 ,其中 是狄利克雷卷积符号。
肯定是积性函数, 也是积性函数,因此 也是积性函数,外面那个 最后再乘就行。
令
现在有两种线性筛来求出 :
一种是只记录 ,试图用 的值来推导出 ,其中 是指 的最小质因子,这种只适合于求非完全积性函数和完全积性函数的狄利克雷卷积,这里两个函数都是非完全积性函数的,因此用另一种方法
第二种方法记录每个数的最小质因子 和最小质因子的幂 , 的意义是对 不断地除以 ,直到无法整除,假设做除法的次数是 那么 。这样可以把一个数分成互质的两部分 和 ,则 ,但是这种方法的缺点是当 时,无法直接求出 ,必须要根据不同函数的特殊性来推导 的值。
设 ,
当 时,
当 时,
当 时,同理
这样就知道了,当 时, 而当 时
然后就可以线性筛了,总的时间复杂度是
代码
//线性筛、莫比乌斯反演
#include <cstdio>
#include <algorithm>
#define maxn 4000010
#define mod 1073741824ll
#define ll long long
using namespace std;
ll prime[maxn], g[maxn], u[maxn], pr[maxn], p[maxn];
bool mark[maxn];
void init()
{
ll i, j, t;
u[1]=1;
for(i=2;i<maxn;i++)
{
if(!mark[i])
{
prime[++*prime]=i;
pr[i]=p[i]=i;
u[i]=(-i+1+mod)%mod;
}
else
{
u[i]=u[i/p[i]]*u[p[i]]%mod;
if(i==pr[i]*pr[i])u[i]=(-pr[i]+mod)%mod;
}
for(j=1;j<=*prime and i*prime[j]<maxn;j++)
{
t=i*prime[j];
mark[t]=1;
pr[t]=prime[j];
if(i%prime[j]==0)p[t]=p[i]*prime[j];
else p[t]=prime[j];
if(i%prime[j]==0)break;
}
}
for(i=1;i<maxn;i++)g[i]=i*(i+1)/2%mod;
for(i=1;i<maxn;i++)u[i]=u[i]*i%mod;
for(i=1;i<maxn;i++)u[i]=(u[i]+u[i-1])%mod;
}
void calc(ll n, ll m)
{
ll t, last;
ll ans=0;
if(n<m)swap(n,m);
for(t=1;t<=m;t=last+1)
{
last=min(n/(n/t),m/(m/t));
ans=(ans+(g[n/t]*g[m/t])%mod*(u[last]-u[t-1]+mod)%mod)%mod;
}
printf("%lld\n",ans);
}
int main()
{
ll T, n, m;
init();
scanf("%lld",&T);
while(T--){scanf("%lld%lld",&n,&m);calc(n,m);}
return 0;
}