Dirichlet 卷积是两个定义域在正整数上的函数的如下运算,符号为 $*$
$(f * g)(n) = \sum_{d|n}f(d)g(\frac{n}{d})$
如果不强调 $n$ 可简写为 $f * g$
常用:
$\mu * 1 = \epsilon$
$\phi * 1 = id$
$\epsilon(n) = [n=1]$
$id(n)=n$
Mobius 反演是基于 Dirichlet 卷积的一种....化简式子的方法?
比较有用的结论就是 $\mu * 1 = [n=1]$
由这个可以引出两个式子
1.如果 $$F(n) = \sum_{n|d}f(d)$$
则 $$f(n) = \sum_{n|d} F(d)\mu(\lfloor \frac{d}{n} \rfloor)$$
2.如果 $$F(n) = \sum_{d|n}f(d)$$
则 $$f(n) = \sum_{d|n} \mu(\lfloor \frac{n}{d} \rfloor)F(d)$$
还有一个很好用的东西叫做数论分块,即 $\lfloor \frac{n}{i} \rfloor$ 只有 $\sqrt{n}$ 种取值
知道这些就可以做题了
bzoj1101 Zap
求$$\sum_{i=1}^n \sum_{j=1}^m[gcd(i,j)==k]$$
sol:先除以 $k$ ,转化为 $$\sum_{i=1}^x \sum_{j=1}^y[gcd(i,j)==1] \space \space (x = \lfloor \frac{n}{k} \rfloor,y=\lfloor \frac{m}{k} \rfloor)$$
然后发现 $[gcd(i,j)==1]$ 是一个 $[n=1]$ 的形式,我们把它转化成 $\mu * 1$
得到
$$\sum_{i=1}^x \sum_{i=1}^y \sum_{d|gcd(i,j)} \mu(d)$$
因为 $d|(gcd(x,y)$等价于 $d|x$ & $d|y$
而且 $1\thicksim n$ 中满足 $d|x$ 的 $x$ 数量为 $\lfloor \frac{n}{x} \rfloor$
所以原式为 $$\sum_{d=1}^x \mu(d) \lfloor \frac{x}{d} \rfloor \lfloor \frac{y}{d} \rfloor$$
预处理 $\mu$ 的前缀和,数论分块即可
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 50010; int ntp[maxn],pri[maxn],mu[maxn],tot; int sum[maxn]; void getmu() { mu[1]=1; for(int i=2;i<=50000;i++) { if(!ntp[i])pri[++tot]=i,mu[i]=-1; for(int j=1;j<=tot&&pri[j]*i<=50000;j++) { ntp[i*pri[j]]=1; if(i%pri[j]==0){mu[i*pri[j]]=0;break;} else mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<=50000;i++)sum[i]=sum[i-1]+mu[i]; } int cal(int x,int y) { int ret = 0; if(x > y)swap(x,y); for(int L=1,R=0;L<=x;L=R+1) { R = min(x/(x/L),y/(y/L)); ret += (x/L) * (y/L) * (sum[R] - sum[L - 1]); }return ret; } int main() { int T = read(); getmu(); while(T--) { int a = read(),b = read(),d = read(); printf("%d\n",cal(a / d,b / d)); } }