P3768 简单的数学题

非常典型的一道杜教筛求前缀和题目

首先我们先顺着题目给出的式子往下面推

i=1nj=1nijgcd(i,j)=d=1ni=1nj=1nijd[gcd(i,j)=d]=d=1nd3i=1ndj=1ndij[gcd(i,j)=1]\sum\limits_{i=1}^n\sum\limits_{j=1}^nijgcd(i,j)=\sum\limits_{d=1}^n\sum\limits_{i=1}^n\sum\limits_{j=1}^nijd[gcd(i,j)=d]\\=\sum\limits_{d=1}^nd^3\sum\limits_{i=1}^{\frac{n}{d}}\sum\limits_{j=1}^{\frac{n}{d}}ij[gcd(i,j)=1]

用莫比乌斯反演,令 g(n)=i=1nj=1nijg(n)=\sum\limits_{i=1}^n\sum\limits_{j=1}^nij

ans=d=1nd3i=1ndμ(i)i2g(nid)\Rightarrow ans=\sum\limits_{d=1}^nd^3\sum\limits_{i=1}^{\frac{n}{d}}\mu(i)i^2g(\frac{n}{id})

T=idT=id,尝试交换循环顺序

ans=T=1ng(nT)T2dTdμ(Td)ans=\sum\limits_{T=1}^ng(\frac{n}{T})T^2\sum\limits_{d|T}d\mu(\frac{T}{d})

dTdμ(Td)\sum\limits_{d|T}d\mu(\frac{T}{d}) 写成迪利克雷卷积的形式 μid=ϕ\mu*id=\phi

ans=T=1ng(nT)T2ϕ(T)\Rightarrow ans=\sum\limits_{T=1}^ng(\frac{n}{T})T^2\phi(T)

f(n)=n2ϕ(n)f(n)=n^2\phi(n)

ans=T=1ng(nT)f(T)ans=\sum\limits_{T=1}^ng(\frac{n}{T})f(T)

对于 gg 可以使用数论分块,考虑如何快速求 S(n)=i=1nf(i)S(n)=\sum\limits_{i=1}^nf(i)

首先搬出杜教筛式子 g(1)S(n)=i=1n(fg)(i)d=2ng(d)S(nd)g(1)S(n)=\sum\limits_{i=1}^n(f*g)(i)-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)

(fg)(n)=dng(d)f(nd)=dnn2d2ϕ(nd)g(d)(f*g)(n)=\sum\limits_{d|n}g(d)f(\frac{n}{d})=\sum\limits_{d|n}\frac{n^2}{d^2}\phi(\frac{n}{d})g(d)

不妨令 g(i)=i2g(i)=i^2

(fg)(n)=dnn2ϕ(nd)=n2dnϕ(nd)=n3(f*g)(n)=\sum\limits_{d|n}n^2\phi(\frac{n}{d})=n^2\sum\limits_{d|n}\phi(\frac{n}{d})=n^3

S(n)=i=1ni3d=2ng(d)S(nd)\Rightarrow S(n)=\sum\limits_{i=1}^ni^3-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)

ans=T=1ng(nT)T2ϕ(T)ans=\sum\limits_{T=1}^ng(\frac{n}{T})T^2\phi(T)

前面那坨可以数论分块,后面那一坨可以杜教筛 O(n23)O(n^{\frac{2}{3}}) 处理前缀和,注意取模

#include<iostream>
#include<cstdio>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int N = 8000001;

bool st[N];
int prime[N], tot;
ll sum[N], p, n, phi[N], inv2, inv6;
unordered_map<ll, ll> S;

void init()
{
phi[1] = 1;
for (int i = 2; i < N; i++)
{
if (!st[i])
prime[++tot] = i, phi[i] = i - 1;
for (int j = 1; i * prime[j] < N; j++)
{
st[i * prime[j]] = true;
if (i % prime[j] == 0)
{
phi[i * prime[j]] = (phi[i] * prime[j]) % p;
break;
}
phi[i * prime[j]] = (phi[i] * (prime[j] - 1)) % p;
}
}
for (int i = 1; i < N; i++)
sum[i] = (sum[i - 1] + (ll)phi[i] * i % p * i % p) % p;
}

ll Sum1(ll x)
{
ll res = (((x + 1) % p * x % p) % p) * inv2 % p;
return res * res % p;
}

ll Sum2(ll x)
{
ll r1 = (x % p * (x + 1) % p) % p;
return ((r1 * ((x + x + 1) % p)) % p * inv6) % p;
}

ll Djf(ll x)
{
if (x < N)
return sum[x];
if (S[x])
return S[x];
ll res = Sum1(x);
for (ll l = 2, r; l <= x; l = r + 1)
{
r = min(x, (x / (x / l)));
res -= (Sum2(r) - Sum2(l - 1))* (Djf(x / l)) % p;
res %= p;
}
return S[x] = (res + p) % p;
}

ll qmi(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}

int main()
{
scanf("%lld%lld", &p, &n);
init();
inv2 = qmi(2, p - 2), inv6 = qmi(6, p - 2);
ll ans = 0;
for (ll l = 1, r; l <= n; l = r + 1)
{
r = min(n, n / (n / l));
ans = (ans + (Sum1(n / l) % p * ((Djf(r) - Djf(l - 1)) % p)) % p + p) % p;
}
printf("%lld", ans);
return 0;
}