小小网格
题目链接:ybtoj高效进阶 21177
题目大意
给你求 ∑i=1~n∑j=1~mφ(gcd(i,j))。
思路
重新写好看点:
∑ i = 1 n ∑ j = 1 m φ ( gcd ( i , j ) ) \sum\limits_{i=1}^n\sum\limits_{j=1}^m\varphi(\gcd(i,j)) i=1∑nj=1∑mφ(gcd(i,j))
∑ d φ ( d ) ∑ i = 1 n ∑ j = 1 m [ gcd ( i , j ) = d ] \sum\limits_{d}\varphi(d)\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=d] d∑φ(d)i=1∑nj=1∑m[gcd(i,j)=d]
∑ d φ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ( i , j ) = 1 ] \sum\limits_{d}\varphi(d)\sum\limits_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum\limits_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}[\gcd(i,j)=1] d∑φ(d)i=1∑⌊dn⌋j=1∑⌊dm⌋[gcd(i,j)=1]
∑ d φ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ p ∣ g c d ( i , j ) μ ( p ) \sum\limits_{d}\varphi(d)\sum\limits_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum\limits_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}\sum\limits_{p|gcd(i,j)}\mu(p) d∑φ(d)i=1∑⌊dn⌋j=1∑⌊dm⌋p∣gcd(i,j)∑μ(p)
∑ d φ ( d ) ∑ p ∑ i = 1 , i ∣ p ⌊ n d ⌋ ∑ j = 1 , j ∣ p ⌊ m d ⌋ μ ( p ) \sum\limits_{d}\varphi(d)\sum\limits_{p}\sum\limits_{i=1,i|p}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum\limits_{j=1,j|p}^{\left\lfloor\frac{m}{d}\right\rfloor}\mu(p) d∑φ(d)p∑i=1,i∣p∑⌊dn⌋j=1,j∣p∑⌊dm⌋μ(p)
∑ d φ ( d ) ∑ p μ ( p ) ⋅ ⌊ n d p ⌋ ⋅ ⌊ m d p ⌋ \sum\limits_{d}\varphi(d)\sum\limits_{p}\mu(p)\cdot\left\lfloor\dfrac{n}{dp}\right\rfloor\cdot\left\lfloor\dfrac{m}{dp}\right\rfloor d∑φ(d)p∑μ(p)⋅⌊dpn⌋⋅⌊dpm⌋
然后设 T = p d T=pd T=pd,然后有:
∑ d φ ( d ) ∑ p μ ( p ) ⋅ ⌊ n T ⌋ ⋅ ⌊ m T ⌋ \sum\limits_{d}\varphi(d)\sum\limits_{p}\mu(p)\cdot\left\lfloor\dfrac{n}{T}\right\rfloor\cdot\left\lfloor\dfrac{m}{T}\right\rfloor d∑φ(d)p∑μ(p)⋅⌊Tn⌋⋅⌊Tm⌋
∑ T = 1 min ( n , m ) ⌊ n T ⌋ ⋅ ⌊ m T ⌋ ⋅ ( ∑ d ∣ T φ ( d ) μ ( T d ) ) \sum\limits_{T=1}^{\min(n,m)}\left\lfloor\dfrac{n}{T}\right\rfloor\cdot\left\lfloor\dfrac{m}{T}\right\rfloor\cdot(\sum\limits_{d|T}\varphi(d)\mu(\dfrac{T}{d})) T=1∑min(n,m)⌊Tn⌋⋅⌊Tm⌋⋅(d∣T∑φ(d)μ(dT))
然后你会发现右边就是一个狄利克雷卷积。
那我们设 g = φ ∗ μ g=\varphi*\mu g=φ∗μ(积性函数,因为是两个积性函数相乘),然后式子就是:
∑ T = 1 min ( n , m ) ⌊ n T ⌋ ⋅ ⌊ m T ⌋ ⋅ g ( d ) \sum\limits_{T=1}^{\min(n,m)}\left\lfloor\dfrac{n}{T}\right\rfloor\cdot\left\lfloor\dfrac{m}{T}\right\rfloor\cdot g(d) T=1∑min(n,m)⌊Tn⌋⋅⌊Tm⌋⋅g(d)
然后你发现左边两个可以数论分块,然后你考虑右边的能不能求前缀和。
看到是积性函数,考虑用杜教筛,设 S ( n ) = ∑ i = 1 n g ( i ) S(n)=\sum\limits_{i=1}^ng(i) S(n)=i=1∑ng(i)。
然后你就要找一个合适的 f f f,不难看出可以用 I ∗ I = d I*I=d I∗I=d。
然后 g ∗ d = φ ∗ μ ∗ I ∗ I = ( φ ∗ I ) ∗ ( μ ∗ I ) = i d ∗ ϵ = i d g*d=\varphi*\mu*I*I=(\varphi*I)*(\mu*I)=id*\epsilon=id g∗d=φ∗μ∗I∗I=(φ∗I)∗(μ∗I)=id∗ϵ=id。
然后就有 d ( 1 ) S ( n ) = ∑ i = 1 n i d ( i ) − ∑ i = 2 n d ( i ) S ( ⌊ n i ⌋ ) = n ( n + 1 ) 2 − ∑ i = 2 n d ( i ) S ( ⌊ n i ⌋ ) d(1)S(n)=\sum\limits_{i=1}^nid(i)-\sum\limits_{i=2}^nd(i)S(\left\lfloor\dfrac{n}{i}\right\rfloor)=\dfrac{n(n+1)}{2}-\sum\limits_{i=2}^nd(i)S(\left\lfloor\dfrac{n}{i}\right\rfloor) d(1)S(n)=i=1∑nid(i)−i=2∑nd(i)S(⌊in⌋)=2n(n+1)−i=2∑nd(i)S(⌊in⌋)
然后 d ( i ) d(i) d(i) 的前缀和可以数论分块,小于 n 2 3 n^{\frac{2}{3}} n32 的预处理前缀和,大于的直接根号求。
然后小小卡个常即可。
代码
#include<map>
#include<cstdio>
#define ll long long
#define mo 1000000007
using namespace std;
const int Maxn = 2000001;
int n, m;
ll d[Maxn + 1], g[Maxn + 1], ans;
int low[Maxn + 1], phi[Maxn + 1], prime[Maxn + 1];
map <int, ll> ans_g;
void init() {
//杜教筛的预处理
g[1] = 1; phi[1] = 1; low[1] = 1;
for (int i = 2; i <= Maxn; i++) {
if (!low[i]) phi[i] = i - 1, low[i] = i, prime[++prime[0]] = i, g[i] = i - 2;
for (int j = 1; j <= prime[0] && i * prime[j] <= Maxn; j++) {
if (i % prime[j]) low[i * prime[j]] = prime[j], phi[i * prime[j]] = phi[i] * (prime[j] - 1), g[i * prime[j]] = g[i] * g[prime[j]];
else {
low[i * prime[j]] = low[i] * prime[j], phi[i * prime[j]] = phi[i] * prime[j];
if (low[i] != i) g[i * prime[j]] = g[i / low[i]] * g[low[i] * prime[j]];
else g[i * prime[j]] = phi[i * prime[j]] + phi[i] * (-1);
break;
}
}
}
for (int i = 1; i <= Maxn; i++)
for (int j = 1; i * j <= Maxn; j++)
d[i * j]++;
for (int i = 1; i <= Maxn; i++)
d[i] += d[i - 1], g[i] += g[i - 1];
}
ll sum_d(ll n) {
//数论分块
if (n <= Maxn) return d[n];
ll re = 0;
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
re += (r - l + 1) * (n / l);
}
return re;
}
ll sum_g(ll n) {
//杜教筛
if (n <= Maxn) return g[n];
if (ans_g[n]) return ans_g[n];
ll re = n * (n + 1) / 2;
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
re -= (sum_d(r) - sum_d(l - 1)) * sum_g(n / l);
}
return ans_g[n] = re;
}
int main() {
// freopen("mesh.in", "r", stdin);
// freopen("mesh.out", "w", stdout);
scanf("%d %d", &n, &m);
init();
for (int l = 1, r; l <= n && l <= m; l = r + 1) {
//数论分块
r = min(n / (n / l), m / (m / l));
ans = (ans + 1ll * (n / l) * (m / l) * (sum_g(r) - sum_g(l - 1))) % mo;
}
printf("%lld", ans);
return 0;
}