您的位置:首页 > 其它

[BZOJ3994][SDOI2015]约数个数和(莫比乌斯反演)

2017-10-18 23:33 357 查看
首先介绍一个性质:∑Ni=1∑Mj=1σ0(ij)=∑Ni=1∑Mj=1⌊Ni⌋⌊Mj⌋[gcd(i,j)=1]。

证明为:

先设上式等号左边为f(N,M),等号右边为g(N,M)。

则有:

f(N,M)−f(N−1,M)−f(N,M−1)+f(N−1,M−1)=σ0(NM)

g(N,M)−g(N−1,M)−g(N,M−1)+g(N−1,M−1)

=∑i∑j(⌊Ni⌋−⌊N−1i⌋)(⌊Mj⌋−⌊M−1j⌋)[gcd(i,j)=1]

可以知道,⌊Ni⌋−⌊N−1i⌋=1当且仅当i|N,⌊Mj⌋−⌊M−1j⌋同理。

也就是说,g(N,M)−g(N−1,M)−g(N,M−1)+g(N−1,M−1)就相当于求∀1≤i≤N,1≤j≤M,满足i|N,j|M,gcd(i,j)=1的数对(i,j)的个数。实际上,这个值等于σ0(NM)。

Q:为什么上面这样做就不会重复枚举约数呢?

A:设k=Ni。如果存在一个kj=ab,a|k,a|N,b|M,则再设k=ax,就有ijx2=Na∗b,又由于此时又有b=jx,所以这时候Na和b一定含有公约数x,即不互质。从上面得出,满足i|N,j|M,gcd(i,j)=1并且Ni∗j为定值的数对只有1个。得证。

得出式子:

Ans=∑Ni=1∑Mj=1⌊Ni⌋⌊Mj⌋[gcd(i,j)=1]

=∑Ni=1∑Mj=1⌊Ni⌋⌊Mj⌋∑d|gcd(i,j)μ(d)

=∑d∑Ni=1,d|i∑Mj=1,d|j⌊Ni⌋⌊Mj⌋μ(d)

=∑dμ(d)∑⌊Nd⌋i=1∑⌊Md⌋j=1⌊Nid⌋⌊Mjd⌋

=∑dμ(d)(∑⌊Nd⌋i=1⌊⌊Nd⌋i⌋)∗(∑⌊Md⌋j=1⌊⌊Md⌋j⌋)。

可以O(nn−−√)预处理出∑xi=1⌊xi⌋的值后,分块解决。

总之,莫比乌斯反演的关键是利用好∑d|nμ(d)=[n=1]这个公式,并把⌊Ni⌋的值分块。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')

a681
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int MaxN = 5e4, N = MaxN + 5;
int miu
, pri
, tot, sum
, Sum
;
bool mark
;
void sieve() {
int i, j; miu[1] = 1; mark[0] = mark[1] = 1;
for (i = 2; i <= MaxN; i++) {
if (!mark[i]) pri[++tot] = i, miu[i] = -1;
for (j = 1; j <= tot; j++) {
if (1ll * i * pri[j] > MaxN) break;
mark[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
else miu[i * pri[j]] = -miu[i];
}
}
for (i = 1; i <= MaxN; i++) sum[i] = sum[i - 1] + miu[i];
for (i = 1; i <= MaxN; i++)
for (j = 1; j <= i;) {
int nxt = i / (i / j);
Sum[i] += i / j * (nxt - j + 1);
j = nxt + 1;
}
}
ll solve(int a, int b) {
int i, n = min(a, b); ll ans = 0;
for (i = 1; i <= n;) {
int nxt = min(a / (a / i), b / (b / i));
ans += 1ll * Sum[a / i] * Sum[b / i] * (sum[nxt] - sum[i - 1]);
i = nxt + 1;
}
return ans;
}
int main() {
int a, b, T = read(); sieve();
while (T--) a = read(), b = read(),
printf("%lld\n", solve(a, b));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: