bzoj3309: DZY Loves Math
Description
对于正整数n,定义f(n)为n所含质因子的最大幂指数。例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0。
给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b)。
Input
第一行一个数T,表示询问数。
接下来T行,每行两个数a,b,表示一个询问。
Output
对于每一个询问,输出一行一个非负整数作为回答。
Sample Input
4
7558588 9653114
6514903 4451211
7425644 1189442
6335198 4957
Sample Output
35793453939901
14225956593420
4332838845846
15400094813
HINT
【数据规模】
T<=10000
1<=a,b<=10^7
分析
ans=∑in∑jmf(gcd(i,j))
a
n
s
=
∑
i
n
∑
j
m
f
(
g
c
d
(
i
,
j
)
)
=∑df(d)∑in∑jm[gcd(i,j)==d]
=
∑
d
f
(
d
)
∑
i
n
∑
j
m
[
g
c
d
(
i
,
j
)
==
d
]
=∑df(d)∑i⌊nd⌋∑j⌊md⌋[gcd(i,j)==1]
=
∑
d
f
(
d
)
∑
i
⌊
n
d
⌋
∑
j
⌊
m
d
⌋
[
g
c
d
(
i
,
j
)
==
1
]
=∑df(d)∑i⌊nd⌋∑j⌊md⌋∑d|i,d|jμ(d)
=
∑
d
f
(
d
)
∑
i
⌊
n
d
⌋
∑
j
⌊
m
d
⌋
∑
d
|
i
,
d
|
j
μ
(
d
)
=∑df(d)∑kμ(k)⌊mkd⌋⌊nkd⌋
=
∑
d
f
(
d
)
∑
k
μ
(
k
)
⌊
m
k
d
⌋
⌊
n
k
d
⌋
=∑df(d)∑kμ(k)⌊mkd⌋⌊nkd⌋
=
∑
d
f
(
d
)
∑
k
μ
(
k
)
⌊
m
k
d
⌋
⌊
n
k
d
⌋
=∑D⌊mD⌋⌊nD⌋∑d|Df(d)μ(Dd)
=
∑
D
⌊
m
D
⌋
⌊
n
D
⌋
∑
d
|
D
f
(
d
)
μ
(
D
d
)
一波推导之后,我们成功找到了一个 O(nlogn+n−−√+m−−√) O ( n l o g n + n + m ) 的算法。
发现会超时,瓶颈在于 O(logn) O ( l o g n ) 的复杂度。
发现 g(D)=∑d|Df(d)μ(Dd) g ( D ) = ∑ d | D f ( d ) μ ( D d ) 是一个狄利克雷卷积,根据套路肯定是线性筛时乱搞。
我们发掘 g(D) g ( D ) 的性质,打表找规律。
0 1 1 1 1 -1 1 1 1 -1 1 0 1 -1 -1 1 1 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
突然发现0的项很少,然后找0的项
1 12 18 20 24 28 40 44 45 48 50
不难发现,这些数各个素因子的次幂不同。
其次我们发现 g g 这个数组只有1和-1,凭直觉就知道和素因子个数有关。
发现奇数个不同的素因子为1,偶数个不同的素因子为0
考虑证明。
将D标准分解,
我们其实把D中的素数分成两半,一半给f,一半给 μ μ
由于如果 μ μ 函数如果里面塞超过两个以上相同素数,那么 μ μ 就是零,没有意义。所以每次最多往 μ μ 里塞同种素数中的一个。
假设 f(D)=a f ( D ) = a ,那么我们选数最多导致 f(Dd)=a或a−1 f ( D d ) = a 或 a − 1
我们把D的素因子分为两个集合A,B,其中 A={pi|ai=a},B={pi|ai≠a} A = { p i | a i = a } , B = { p i | a i ≠ a }
非常显然地,对于每个从A集合中选若干个数的方案,从B集合选若干个数得到的所有方案的选数的个数的奇偶方案相同。由于 μ μ 的性质会抵消掉。
只有一种特殊情况,就是 B=∅ B = ∅ ,这个时候考虑A集合的选取,假设我们不管f值的变化,那么A集合的奇偶方案仍然是相同的,会两两被 μ μ 抵消,可是当A集合全部选的时候,f的值会减一,所以最后的答案会是1或-1,由其因数个数决定。
证明完以上性质后,我们在线性筛的时候递推就好了,具体看代码。
代码
/**************************************************************
Problem: 3309
User: 2014lvzelong
Language: C++
Result: Accepted
Time:11360 ms
Memory:206368 kb
****************************************************************/
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
const int N = 1e7 + 10;
int read() {
char ch = getchar(); int x = 0, f = 1;
for(;ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch;
return x * f;
}
bool vis[N]; int pri[N], cnt[N], mn[N], top, n, m;
long long g[N];
void Init(int N) {
for(int i = 2;i <= N; ++i) {
if(!vis[i]) {cnt[i] = 1; mn[i] = i; g[i] = 1; pri[++top] = i;}
for(int j = 1, c = (i << 1);j <= top && c <= N; c = i * pri[++j]) {
vis[c] = true;
if(i % pri[j]) {
cnt[c] = 1; mn[c] = pri[j];
g[c] = (cnt[i] == 1 ? -g[i] : 0);
}
else {
cnt[c] = cnt[i] + 1;
mn[c] = mn[i] * pri[j]; int d = i / mn[i];
if(d == 1) g[c] = 1;
else g[c] = (cnt[c] == cnt[d] ? -g[d] : 0);
break;
}
}
}
for(int i = 1;i <= N; ++i) g[i] += g[i - 1];
}
int main() {
Init(1e7);
for(int T = read(); T--; ) {
n = read(); m = read(); long long ans = 0;
for(int i = 1, pos;i <= min(n, m); i = pos + 1) {
pos = min(n / (n / i), m / (m / i));
ans += 1LL * (n / i) * (m / i) * (g[pos] - g[i - 1]) ;
}
printf("%lld\n", ans);
}
return 0;
}