P3327 的小推导
学杜教筛了才发现莫反都没学到家 QwQ
目标:
\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}d(ij)
\]
单次要到 \(O(\sqrt n)\)。
\[=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{x \mid i}\sum\limits_{y \mid j}[(x,y)=1]
\]
\[=\sum\limits_{x=1}^{n}\sum\limits_{y=1}^{m}\lfloor\dfrac{n}{x}\rfloor\lfloor\dfrac{n}{y}\rfloor[(x,y)=1]
\]
此时再换只会更复杂,考虑对 \(1\) 进行反演。
设
\[f(x)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\lfloor\dfrac{n}{i}\rfloor\lfloor\dfrac{n}{j}\rfloor[(i,j)=x]
\]
\[g(x) = \sum\limits_{x \mid d} f(d) \iff f(x) = \sum \limits_{x \mid d}\mu(\dfrac{d}{x})g(d)
\]
明显我们只用求 \(f(1)\),于是要把 \(g(x)\) 在可接受的复杂度内弄出来。
\[f(1)=\sum\limits_{i=1}^n\mu(i)g(i)
\]
\[g(x)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\lfloor\dfrac{n}{i}\rfloor\lfloor\dfrac{n}{j}\rfloor[x \mid (i,j)]
\]
\[=\sum\limits_{x=1}^n\sum\limits_{i=1}^{\lfloor\frac{n}{x}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{x}\rfloor}\lfloor\dfrac{n}{ix}\rfloor\lfloor\dfrac{n}{jx}\rfloor
\]
换元
\[s_n=\sum\limits_{i=1}^{n}\lfloor\frac{n}{i}\rfloor
\]
上式即为
\[=\sum\limits_{x=1}^n s_{\lfloor\frac{n}{x}\rfloor}s_{\lfloor\frac{n}{y}\rfloor}
\]
推导结束 。
关键的一步在于把 \([(i,j)=x]\) 变为了 \([x \mid (i,j)]\),可以少套一个 \(\mu\),易于变形。
对 \(g(x)\) 整除分块,套个 \(\mu\) 的前缀和,即可在 \(O(\sqrt{n})\) 的复杂度内解决。
等写完补代码。
upd 16:21 一遍过,真丝滑
#include <cstdio>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
const int M = 100005;
int mu[M], p[M], tt, summu[M]; LL s[M]; bool f[M];
void pre(int n){
summu[1] = f[1] = mu[1] = 1;
for(int i = 2; i <= n; i++){
if(!f[i]) p[++tt] = i, mu[i] = -1;
for(int j = 1; j <= tt; j++){
int k = p[j] * i; if(k > n) break;
if(i % p[j] == 0) {f[k] = 1; mu[k] = 0; break;}
f[k] = 1; mu[k] = -mu[i];
}
summu[i] = summu[i-1] + mu[i];
}
for(int i = 1; i <= n; i++){
for(int l = 1, r; l <= i; l = r+1){
r = i/(i/l); s[i] += 1ll *(r-l+1) * (i/l);
}
}
}
LL solve(int n, int m){
if(n > m) swap(n, m); LL ans = 0;
for(int l = 1, r; l <= n; l = r+1){
r = min(m/(m/l), n/(n/l));
ans += (summu[r] - summu[l-1]) * s[n/l] * s[m/l];
}
return ans;
}
int T, n, m;
int main(){
scanf("%d", &T); pre(50000);
while(T--){
scanf("%d %d", &n, &m);
printf("%lld\n", solve(n, m));
}
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/16344173.html