Loading

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));
    }
}
posted @ 2022-06-05 15:58  purplevine  阅读(25)  评论(0编辑  收藏  举报