求:
i=1∑nj=1∑mφ(ij)
先要考虑怎么把 φ 转成带有 gcd 或 lcm 的形式。
性质:φ(ij)=φ(gcd(i,j))φ(i)φ(j)gcd(i,j)。
证明:
φ(i)φ(j)=ip∣i∏pp−1jp∣j∏pp−1=ijp∣ij∏pp−1p∣gcd(i,j)∏pp−1p∈primes
所以有:
φ(i)φ(j)gcd(i,j)=ijp∣ij∏pp−1gcd(i,j)p∣gcd(i,j)∏pp−1=φ(ij)φ(gcd(i,j))
化简式子,有:
i=1∑nj=1∑mφ(ij)=i=1∑nj=1∑mφ(gcd(i,j))φ(i)φ(j)gcd(i,j)=d=1∑nφ(d)di=1∑nj=1∑mφ(i)φ(j)[gcd(i,j)=d]=d=1∑nφ(d)di=1∑⌊dn⌋j=1∑⌊dm⌋φ(id)φ(jd)[gcd(i,j)=1]=d=1∑nφ(d)dp=1∑⌊dn⌋μ(p)i=1∑⌊dn⌋φ(id)[p∣i]j=1∑⌊dm⌋φ(jd)[p∣j]=d=1∑nφ(d)dp=1∑⌊dn⌋μ(p)i=1∑⌊kdn⌋φ(ikd)j=1∑⌊kdm⌋φ(jkd)=T=1∑nd∣T∑φ(d)dμ(pT)i=1∑⌊Tn⌋φ(iT)j=1∑⌊Tm⌋φ(jT)设 T=dp
设 f(n)=d∣n∑φ(d)dμ(pn),线性筛预处理即可,O(nlnn)。
设 g(k,n)=i=1∑nφ(i,k),显然 g(k,n)=g(k,n−1)+φ(nk)。
则原式等于:
T=1∑nf(T)×g(T,⌊Tn⌋)×g(T,⌊Tm⌋)
发现整除分块不了,考虑把整个式子设出来:
h(a,b,n)=t=1∑nf(t)×g(t,a)×g(t,b)
容易发现,这其实就是一个差分:
h(a,b,n)=⌊ln⌋=⌊rn⌋and⌊lm⌋=⌊rm⌋∑h(⌊rn⌋,⌊rm⌋,r)−h(⌊rn⌋,⌊rm⌋,l)
再考虑根号分治,我们设一个阈值 S,将所有 h(1,1,1)∼h(S,S,n) 的 h 值预处理出来。
预处理式子就是:
h(j,k,i)=h(j,k,i−1)+f(i)×g(i,j)×g(i,k)
对于 ⌊rn⌋≤S 可直接查询。
否则,可知 r≤⌊Sn⌋,数论分块计算即可。
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int S = 50;
const int maxn = 1e5;
bool vis[maxn + 7];
int tot, prime[maxn + 7];
int mu[maxn + 7], phi[maxn + 7], invphi[maxn + 7];
int sum[maxn + 7];
int *g[maxn + 7], *t[100][100];
int qpow(int x, int y)
{
int res = 1;
while (y)
{
if (y & 1)
res = res * (long long)x % mod;
x = x * (long long)x % mod;
y >>= 1;
}
return res;
}
void init()
{
phi[1] = mu[1] = invphi[1] = 1;
for (int i = 2; i <= maxn; i++)
{
if (!vis[i])
prime[++tot] = i, phi[i] = i + (mu[i] = -1);
for (int j = 1; j <= tot && i * prime[j] <= maxn; j++)
{
vis[i * prime[j]] = true;
if (i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
mu[i * prime[j]] = -mu[i];
}
invphi[i] = qpow(phi[i], mod - 2);
}
for (int pp = 1; pp <= maxn; pp++)
for (int q = pp, d = 1; q <= maxn; q += pp, d++)
sum[q] = (sum[q] + pp * (long long)invphi[pp] % mod * mu[d]) % mod, sum[q] += (sum[q] < 0 ? mod : 0);
for (int i = 1; i <= maxn; i++)
{
g[i] = new int[(maxn / i) + 1], g[i][0] = 0;
for (int j = 1, sb = maxn / i; j <= sb; j++)
g[i][j] = (g[i][j - 1] + phi[i * j]) % mod;
}
for (int j = 1; j <= S; j++)
for (int k = j; k <= S; k++)
{
int len = maxn / max(j, k);
t[j][k] = new int[len + 1], t[j][k][0] = 0;
for (int i = 1; i <= len; i++)
t[j][k][i] = (t[j][k][i - 1] + sum[i] * (long long)g[i][j] % mod * g[i][k] % mod) % mod;
}
}
signed main()
{
init();
int T;
scanf("%d", &T);
while (T--)
{
int n, m, res = 0;
scanf("%d%d", &n, &m);
if (n > m)
swap(n, m);
for (int i = 1, kkk = m / S; i <= kkk; i++)
res = (res + sum[i] * (long long)g[i][n / i] % mod * g[i][m / i] % mod) % mod;
for (int i = m / S + 1, j; i <= n; i = j + 1)
{
j = min(n / (n / i), m / (m / i));
res = (res + t[n / i][m / i][j] - t[n / i][m / i][i - 1]) % mod, res += (res < 0 ? mod : 0);
}
printf("%d\n", res);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】