[题解] HDU 5528 Count a*b 简单容斥+莫比乌斯反演
题意:令
f
(
m
)
f(m)
f(m)表示有多少个整数有序二元组
(
a
,
b
)
(a,b)
(a,b)满足
0
≤
a
,
b
<
m
0\le a,b<m
0≤a,b<m,且
m
∤
a
b
m\nmid ab
m∤ab。
输入
n
n
n,求
g
(
n
)
=
∑
m
∣
n
f
(
m
)
(
m
o
d
2
64
)
g(n)=\sum_{m|n}f(m) ( mod2^{64})
g(n)=∑m∣nf(m)(mod264) (也就是用
u
n
s
i
g
n
e
d
l
o
n
g
l
o
n
g
unsigned \ long \ long
unsigned long long)。
T
T
T组数据,
1
≤
T
≤
20000
,
1
≤
n
≤
1
0
9
1\le T \le 20000, 1\le n \le 10^9
1≤T≤20000,1≤n≤109。
这道题对于其他莫比乌斯反演的题目的不同之处在于,此题借助实际意义来推式子,从而避免了大量运算,得到了极大的简化。这启示我们,遇到一道题,先尝试用意义去理解,而不要去傻推式子。
我们可以计算
m
∣
a
b
m|ab
m∣ab的二元组的个数,设为
h
(
m
)
h(m)
h(m).那么
g
(
n
)
=
∑
m
∣
n
m
2
−
∑
m
∣
n
h
(
m
)
g(n) = \sum_{m|n}m^2-\sum_{m|n}h(m)
g(n)=m∣n∑m2−m∣n∑h(m)
对于第一个函数,其等于
(
1
+
p
1
2
+
p
1
4
+
⋯
+
p
1
2
α
1
)
⋅
(
1
+
p
2
2
+
p
2
4
+
⋯
+
p
2
2
α
2
)
⋅
⋯
⋅
(
1
+
p
k
2
+
p
k
4
+
⋯
+
p
k
2
α
k
)
(1+p_1^2+p_1^4+\dots+p_1^{2\alpha_1})\cdot\left(1+p_2^2+p_2^4+\dots+p_2^{2\alpha_2}\right)\cdot\dots\cdot(1+p_k^2+p_k^4+\dots+p_k^{2\alpha_k})
(1+p12+p14+⋯+p12α1)⋅(1+p22+p24+⋯+p22α2)⋅⋯⋅(1+pk2+pk4+⋯+pk2αk)
对于每一个
n
n
n,我们先预处理好
1
1
1到
1
0
6
10^6
106的素数,然后拿去作试除法(直接试除法时间复杂度
O
(
n
)
O(\sqrt{n})
O(n)会超时),计算出幂次,然后直接算出每个
p
p
p的贡献。
这里用了一种暴力统计积性函数卷积的新方法,值得注意。
对于第二个函数,记
d
=
gcd
(
m
,
a
)
d=\gcd(m,a)
d=gcd(m,a),则
1
=
gcd
(
m
d
,
a
d
)
1 = \gcd\left(\dfrac{m}{d},\dfrac{a}{d}\right)
1=gcd(dm,da)
m
∣
a
b
⟺
m
d
∣
a
d
b
⟺
m
d
∣
b
m\mid ab\iff \dfrac{m}{d}\mid \dfrac{a}{d}b\iff \dfrac{m}{d}\mid b
m∣ab⟺dm∣dab⟺dm∣b
对于每一个
d
d
d,
b
b
b是
m
d
\dfrac{m}{d}
dm的倍数。于是我们可以找出
m
m
d
=
d
\dfrac{m}{\frac{m}{d}}=d
dmm=d个
b
b
b。
(这里我们把0看做
m
m
m,不妨碍计数)
现在考虑如何生成
a
a
a。对于每一个
d
d
d,都有
gcd
(
m
d
,
a
d
)
=
1
\gcd\left(\dfrac{m}{d},\dfrac{a}{d}\right)=1
gcd(dm,da)=1,其数量为
φ
(
m
d
)
\varphi\left(\dfrac{m}{d}\right)
φ(dm)。
因此
h
(
m
)
=
∑
d
∣
m
d
φ
(
m
d
)
h(m)=\sum_{d|m}d\varphi\left(\dfrac{m}{d}\right)
h(m)=d∣m∑dφ(dm)
而
∑
m
∣
n
h
(
m
)
=
∑
m
∣
n
∑
d
∣
m
d
φ
(
m
d
)
=
k
d
∣
n
m
=
k
d
∑
d
∣
n
d
∑
k
∣
n
d
φ
(
k
)
=
φ
∗
1
=
i
d
∑
d
∣
n
d
⋅
n
d
=
n
∑
d
∣
n
1
\begin{aligned} \sum_{m|n}h(m) &=\sum_{m|n}\sum_{d|m}d\varphi\left(\dfrac{m}{d}\right)\\ &\xlongequal[kd|n]{m=kd}\sum_{d|n}d\sum_{k|\frac{n}{d}}\varphi\left(k\right)\\ &\xlongequal[]{\varphi * 1 = id}\sum_{d|n}d\cdot \dfrac{n}{d}\\ &=n\sum_{d|n}1\\ \end{aligned}
m∣n∑h(m)=m∣n∑d∣m∑dφ(dm)m=kdkd∣nd∣n∑dk∣dn∑φ(k)φ∗1=idd∣n∑d⋅dn=nd∣n∑1
我们依然采用素数试除法计算约数个数。
时间复杂度
O
(
1
0
6
+
T
π
(
n
)
)
O(10^6+T\pi\left(\sqrt{n}\right))
O(106+Tπ(n)),其中
π
(
n
)
\pi(n)
π(n)为
n
n
n的素数个数函数,
π
(
n
)
∼
n
ln
n
\pi(n)\sim \dfrac{n}{\ln n}
π(n)∼lnnn,故时间复杂度为
O
(
1
0
6
+
T
n
log
n
)
O(10^6+T\dfrac{\sqrt{n}}{\log n})
O(106+Tlognn)。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#define pii pair<int,int>
using namespace std;
typedef unsigned long long ull;
const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 1e6 + 10;
int t;
int p[maxn],cnt;
bool isp[maxn];
void getp(){
isp[1] = 1;
for(int i = 2; i < maxn; i++){
if(!isp[i]) p[++cnt] = i;
for(int j = 1; j <= cnt && i * p[j] < maxn; j++){
isp[i*p[j]] = 1;
if(i%p[j] == 0) break;
}
}
}
inline ull qpow(ull a, int b){
ull ans = 1, base = a;
while(b){
if(b&1) ans *= base;
base *= base, b >>= 1;
}
return ans;
}
int n;
void solve(){
getp();
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int tmp = n;
ull ans1 = 1, ans2 = n;
for(int i = 1; i <= cnt && p[i]*p[i] <= n; i++){
if(tmp%p[i] == 0){
int res = 0;
while(tmp%p[i] == 0){
res++;
tmp /= p[i];
}
ull tmpres = 1, base = p[i]*p[i];
for(int j = 1; j <= res; j++){
tmpres += base;
base = base*(ull)p[i]*(ull)p[i];//不加ull会炸
}
//ans1 *= (qpow(p[i],2*res+2)-1)/((ull)p[i]*(ull)p[i]-1);
//用快速幂会炸
ans2 *= (res+1);
}
}
if(tmp != 1) ans1 *= (1+(ull)tmp*(ull)tmp), ans2 *= 2;
printf("%llu\n",ans1-ans2);
}
}
int main()
{
solve();
return 0;
}
不得不说,这道题把常数卡到了极致(悲)