P3327 [SDOI2015]约数个数和 莫比乌斯反演
链接
思路
第一个式子我也不会,luogu有个证明,自己感悟吧。
[d(ij)=sumlimits_{x|i}sumlimits_{y|j}[gcd(x,y)==1]
]
[sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}sumlimits_{x|i}sumlimits_{y|j}[gcd(x,y)==1]
]
[sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}{left lfloor frac{n}{i}
ight
floor left lfloor frac{m}{j}
ight
floor left [ gcd(i,j)==1
ight ]}
]
[f(x)=sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}{left lfloor frac{n}{i}
ight
floor left lfloor frac{m}{j}
ight
floor left [ gcd(i,j)==x
ight ]}
]
[g(x)=sumlimits_{x|d} f(d)
]
[g(x)=sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}{left lfloor frac{n}{i}
ight
floor left lfloor frac{m}{j}
ight
floor left [ x|gcd(i,j)
ight ]}
]
[g(x)=sumlimits_{i=1}^{frac{n}{x}}sumlimits_{j=1}^{frac{m}{x}}{left lfloor frac{n}{x*i}
ight
floor left lfloor frac{m}{x*j}
ight
floor }
]
[g(x)=sumlimits_{i=1}^{frac{n}{x}}{left lfloor frac{n}{x*i}
ight
floor }sumlimits_{j=1}^{frac{m}{x}}{left lfloor frac{m}{x*j}
ight
floor }
]
[g(x)=sumlimits_{i=1}^{N}{left lfloor frac{N}{i}
ight
floor }sumlimits_{j=1}^{M}{left lfloor frac{M}{j}
ight
floor }(N=n/x,M=m/x)
]
整除分块预处理,O(1)查询g(x)
[f(x)=sumlimits_{x|d}mu(frac{d}{n})g(d)
]
所求$$f(1)=sumlimits_{d=1}^{min(m,n)}mu(d)g(d)$$
g是可以整除分块的
其他
改马蜂,加空格
代码
#include <bits/stdc++.h>
const int N = 5e5+7;
using namespace std;
int read() {
int x = 0, f = 1; char s = getchar();
for (;s > '9' || s < '0'; s = getchar()) if (s == '-') f = -1;
for (;s >= '0' && s <= '9'; s = getchar()) x = x * 10 + s - '0';
return x * f;
}
int n, m, T;
int pri[N], vis[N], tot, mu[N], g[N];
void Euler(int limit) {
mu[1] = 1;
for (int i = 2; i <= limit; ++i) {
if (!vis[i]) {
pri[++tot] = i;
mu[i] = -1;
}
for (int j = 1; j <= tot && i * pri[j] <= limit; ++j) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) {
mu[i * pri[j]] = 0;
break;
}
mu[i * pri[j]] = -mu[i];
}
}
for (int i = 1; i <= limit; ++i) {
for (int l = 1, r; l <= i; l = r + 1) {
r = i / (i / l);
g[i] += (r - l + 1) * (i / r);
}
mu[i] += mu[i - 1];
}
}
void solve() {
n = read(), m = read();
if (n > m) swap(n, m);
long long ans = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += 1LL * (mu[r] - mu[l-1]) * (1LL * g[n/l] * g[m/l]);
}
printf("%lld
", ans);
}
int main() {
Euler(50000);
int T = read();
while (T--) solve();
return 0;
}