设(d(x))表示x约数个数,给定n,m,(sum_{i=1}^nsum_{j=1}^md(ij))
多组询问,1<=T<=50000,1<=N, M<=50000
前置知识:(d(ij)=sum_{x|i}sum_{y|j}[gcd(x,y)=1])
证明:(xy)一定是(ij)的约数。考虑一个质数(p),(i)中包含(p^a),(j)中包含(p^b),则(ij)中包含的是(p^{a+b})。若(gcd(x,y)=1),说明(x,y)中至少有一个数的(p^k)为1,容斥一下就有(a+b+1)种,而(p^{a+b})中也恰好有(a+b+1)的贡献。最后把所有质数贡献乘起来就是答案。
推式子即可:
(sum_{i=1}^nsum_{j=1}^msum_{x|i}sum_{y|j}[gcd(x,y)=1])
(=sum_{x=1}^nsum_{y=1}^mlfloorfrac nx floorlfloorfrac my floor[gcd(x,y)=1])
(=sum_{x=1}^nsum_{y=1}^mlfloorfrac nx floorlfloorfrac my floorsum_{d|x,d|y}mu(d))
(=sum_{d=1}^nmu(d)sum_{x=1}^{n/d}sum_{y=1}^{m/d}lfloorfrac n{xd} floorlfloorfrac m{yd} floor)
设(f(n)=sum_{i=1}^nlfloorfrac ni floor=sum_{i=1}^nd(i)),这里的(d)是约数个数,由于(nle50000),这个可以预处理线性筛约数个数
则原式=(sum_{d=1}^nmu(d)f(lfloorfrac nd floor)f(lfloorfrac md floor)),直接上数论分块
#include <cstdio>
#include <functional>
using namespace std;
int prime[50010], fuck = 50000, tot, d[50010], d1[50010], mu[50010];
bool vis[50010];
int main()
{
mu[1] = d[1] = d1[1] = 1;
for (int i = 2; i <= fuck; i++)
{
if (vis[i] == false) { prime[++tot] = i, mu[i] = -1, d[i] = d1[i] = 2; }
for (int j = 1; j <= tot && i * prime[j] <= fuck; j++)
{
vis[i * prime[j]] = true;
if (i % prime[j] == 0)
{
d1[i * prime[j]] = d1[i] + 1;
d[i * prime[j]] = d[i] / d1[i] * d1[i * prime[j]];
break;
}
d1[i * prime[j]] = 2;
d[i * prime[j]] = d[i] * 2;
mu[i * prime[j]] = -mu[i];
}
d[i] += d[i - 1];
mu[i] += mu[i - 1];
}
int t; scanf("%d", &t);
while (t --> 0)
{
int n, m;
long long ans = 0;
scanf("%d%d", &n, &m);
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 += (mu[j] - mu[i - 1]) * (long long)d[n / i] * d[m / i];
}
printf("%lld
", ans);
}
return 0;
}
45行一遍AC
最近做数论题都不用观察题解了。。。