[SDOI2015]约数个数和

如题,设$d(x)$表示$x$的约数个数,求
首先要知道约数个数的一种表示方法:
然后题目所求就变成了

化简,改变量之后式子变为

考虑莫比乌斯反演,设

考虑优化算法时间复杂度,可以预处理出$\sum_{i = 1}^N(\frac Ni)$和$\sum_{j = 1}^M (\frac Mj)$,连带着莫比乌斯函数可以直接O(N)预处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
inline void Init() {
U[1] = F[1] = 1 ;
for (int i = 2 ; i <= MAXN ; i ++) V[i] = true ;
for (int i = 2 ; i <= MAXN ; i ++) {
if (V[i]) U[i] = - 1, P[++ Tot] = i, F[i] = 2, G[i] = 1 ;
for (int j = 1 ; j <= Tot && i * P[j] < MAXN ; j ++) {
V[i * P[j]] = false ;
if (i % P[j] == 0) {
U[i * P[j]] = 0 ;
F[i * P[j]] = F[i] / (G[i] + 1) * (G[i] + 2) ;
G[i * P[j]] = G[i] + 1 ;
break ;
} else
U[i * P[j]] = - U[i], F[i * P[j]] = (F[i] << 1), G[i * P[j]] = 1 ;
}
}
for (int i = 1 ; i <= MAXN ; i ++)
S[i] = S[i - 1] + U[i], Sum[i] = Sum[i - 1] + F[i] ;
}

问题答案化简为$f(1)$。反演后得到

进行数论分块就可以了,总体时间复杂度$O(T\sqrt N)$,其中T为数据组数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std ;

typedef long long LL ;
const int MAXN = 50010 ;
const int MAXM = 50010 ;

int N, M, U[MAXN], P[MAXN], Tot ;
int S[MAXN], Sum[MAXN], F[MAXN], G[MAXN] ;
bool V[MAXN] ; long long Ans ;

inline int Read() {
int X = 0, F = 1 ; char ch = getchar() ;
while (ch > '9' || ch < '0') F = (ch == '-' ? - 1 : 1), ch = getchar() ;
while (ch >= '0' && ch <= '9') X=(X<<1)+(X<<3)+(ch^48), ch = getchar() ;
return X * F ;
}

inline void Init() {
U[1] = F[1] = 1 ;
for (int i = 2 ; i <= MAXN ; i ++) V[i] = true ;
for (int i = 2 ; i <= MAXN ; i ++) {
if (V[i]) U[i] = - 1, P[++ Tot] = i, F[i] = 2, G[i] = 1 ;
for (int j = 1 ; j <= Tot && i * P[j] < MAXN ; j ++) {
V[i * P[j]] = false ;
if (i % P[j] == 0) {
U[i * P[j]] = 0 ;
F[i * P[j]] = F[i] / (G[i] + 1) * (G[i] + 2) ;
G[i * P[j]] = G[i] + 1 ;
break ;
} else
U[i * P[j]] = - U[i], F[i * P[j]] = (F[i] << 1), G[i * P[j]] = 1 ;
}
}
for (int i = 1 ; i <= MAXN ; i ++)
S[i] = S[i - 1] + U[i], Sum[i] = Sum[i - 1] + F[i] ;
}

int main() {
int T = Read() ; Init () ;
while (T --) { Ans = 0 ;
N = Read() ; M = Read() ;
for (int l = 1, r ; l <= min(N, M) ; l = r + 1) {
r = min(N / (N / l), M / (M / l)) ;
Ans += 1ll * (S[r] - S[l - 1]) * Sum[N / l] * Sum[M / l] ;
} printf("%lld\n", Ans) ;
}
}

本文标题:[SDOI2015]约数个数和

文章作者:Sue Shallow

发布时间:2019年02月13日 - 10:55:53

最后更新:2019年11月11日 - 19:54:16

原始链接:http://Yeasion.github.io/2019/02/13/SDOI2015-约数个数和/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。