P3327 [SDOI2015]約數個數和
一句話題意:
求 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m} d(ij)\)
題解:
首先 \(d\) 函式有個性質, \(d(i,j) == \displaystyle\sum_{x \mid i}\sum_{y\mid j} [gcd(i,j) == 1]\)
然後我們要求的就是 :
\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y\mid j} [gcd[i,j] == 1]\)
直接上莫比烏斯反演,變成:
\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \mid j} \sum_{p\mid x,p\mid y} \mu(p)\)
考慮到這麼多的列舉量不太好算,所以我們需要減少一下列舉量。
先列舉一下 \(x\) 和 \(y\) 試試,變成:
\(\displaystyle\sum_{x=1}^{n}\sum_{j=1}^{m}\sum_{x \mid i}\sum_{y\mid j}\sum_{p\mid x,p \mid y} \mu(p)\)
發現倒數第三個和倒數第四個其實求的是 \(1-n\) 中 \(x\) 的倍數,以及 \(1-m\) 中 \(y\) 的倍數。
就可以寫成:
\(\displaystyle\sum_{x=1}^{n}\sum_{y=1}^{m}\lfloor {n\over x}\rfloor\lfloor{m \over j}\rfloor \sum_{p \mid x,p\mid j} \mu(p)\)
先列舉一下 \(p\) 試試,
\(\displaystyle\sum_{p=1}^{n}\mu(p) \sum_{x=1}^{n}[p\mid x]\sum_{j=1}^{m} [p\mid j] \lfloor {n\over x}\rfloor\lfloor{m\over j}\rfloor\)
中間那兩個柿子還可以在化簡一下變成:
\(\displaystyle\sum_{p=1}^{n}\mu(p)\sum_{x=1}^{n\over d}\sum_{y=1}^{y\over d} \lfloor{n\over px}\rfloor\lfloor{m\over py}\rfloor\)
把兩個向下取整拆開變為:
\(\displaystyle\sum_{p=1}^{n}\mu(p)\sum_{x=1}^{n\over p} \lfloor{n\over px}\rfloor\sum_{j=1}^{m\over p}\lfloor{m\over py}\rfloor\)
設 \(g(n)\) 表示 \(\displaystyle\sum_{i=1}^{n} {n\over i}\)
那麼上面的柿子可以寫成 \(\displaystyle\sum_{p=1}^{n}\mu(p) g({n\over p}) g({m\over p})\)
對於 \(g\) 我們可以利用整除分塊求出來,最後的柿子也要搞個整除分塊,所以就是整除分塊套整除分塊(老千層餅了)
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
const int N = 50010;
int n,m,T,tot;
LL mu[N],sum[N],prime[N];
bool check[N];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
return s * w;
}
void YYCH()
{
mu[1] = 1;
for(int i = 2; i <= N-5; i++)
{
if(!check[i])
{
prime[++tot] = i;
mu[i] = -1;
}
for(int j = 1; j <= tot && i * prime[j] <= N-5; j++)
{
check[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
for(int i = 2; i <= N-5; i++) mu[i] += mu[i-1]; //求mu的字首和
for(int i = 1; i <= N-5; i++)
{
for(int l = 1, r; l <= i; l = r+1)//求g函式值
{
r = min(i,(i/(i/l)));
sum[i] += (r-(l-1)) * (i/l);
}
}
}
LL slove(int n,int m)
{
LL res = 0;
for(int l = 1, r; l <= n; l = r+1)//套個整除分塊
{
r = min(n/(n/l),m/(m/l));
res += (mu[r]-mu[l-1]) * sum[n/l] * sum[m/l];
}
return res;
}
int main()
{
T = read(); YYCH();
while(T--)
{
n = read(); m = read();
if(n > m) swap(n,m);
printf("%lld\n",slove(n,m));
}
return 0;
}