版权属于Enzymii,想要引用此题(包括题面)的朋友请联系博主
分析:
首先要好好读题,题目有一点问题
不过不要在意这么多细节,实际上题目要求:
d的暴力计算公式
n=∏pkii n = ∏ p i k i
d(n)=∑(ki+1) d ( n ) = ∑ ( k i + 1 )
ZYXZYXZYX和我说有一个60%的做法,简单反演一下就可以了:
首先,关于
d
d
函数(约束个数)有一个玄学公式
显然
∑i|k∑d|i∑j|k∑d|j1
∑
i
|
k
∑
d
|
i
∑
j
|
k
∑
d
|
j
1
就是
μ(d)
μ
(
d
)
的计算次数
而通过观察我们可以发现
∑i|k∑d|i
∑
i
|
k
∑
d
|
i
和
∑j|k∑d|j
∑
j
|
k
∑
d
|
j
实际上是等价的
下面我们考虑如何计算
∑i|k∑d|i1
∑
i
|
k
∑
d
|
i
1
上式的意义:i是k的约数,d是i的约数
设
i=pd
i
=
p
d
,则有
仔细观察上式,意义即为: kd k d 的约数个数和,即 d(kd) d ( k d )
因此原式可化为:
满分做法
我们要用到暴力计算d的公式
设
n=∏ki=1paii
n
=
∏
i
=
1
k
p
i
a
i
,则
n2=∏ki=1p2aii
n
2
=
∏
i
=
1
k
p
i
2
a
i
因为
n=∏ki=1paii
n
=
∏
i
=
1
k
p
i
a
i
,那么
n
n
的约数中的幂范围就是
0−ai
0
−
a
i
因为
n2=∏ki=1p2aii
n
2
=
∏
i
=
1
k
p
i
2
a
i
,那么
n2
n
2
的约数中
pi
p
i
的幂范围就是
0−2ai
0
−
2
a
i
n
n
的约数一定是的约数,那么我们考虑
n2
n
2
的约数
X
X
,但是不是
n
n
的约数
那么一定可以表示为
意义即为:X比n的某个约数多乘上若干个 paii p i a i
那么到底是多乘上了那些 paii p i a i 呢?我们可以枚举
设
n
n
的不同约数有个
那么这
ω(n)
ω
(
n
)
个元素能组成的集合个数:
对于上面的 2ω(n) 2 ω ( n ) 集合,我们都可以让ta与 n n 的任何一个约数重组出的合法约数
举个例子:
n=pa11pa22 n = p 1 a 1 p 2 a 2
n2=p2a11p2a22 n 2 = p 1 2 a 1 p 2 2 a 2
ω(n)=2 ω ( n ) = 2
那么我们能得到的集合为 {{0,0},{0,1},{1,0},{1,1}} { { 0 , 0 } , { 0 , 1 } , { 1 , 0 } , { 1 , 1 } }
以n的一个约数 p11p12 p 1 1 p 2 1 为基准,上面的集合与约数 p11p12 p 1 1 p 2 1 能组成的 n2 n 2 的约数分别为:
p11p12,p11pa2+12,pa1+11p12,pa1+11pa2+12 p 1 1 p 2 1 , p 1 1 p 2 a 2 + 1 , p 1 a 1 + 1 p 2 1 , p 1 a 1 + 1 p 2 a 2 + 1
考虑如何快速的计算
2ω(n)
2
ω
(
n
)
观察这些集合的特点,集合元素非0即1
举个例子:
n=pa11pa22pa33 n = p 1 a 1 p 2 a 2 p 3 a 3
ω(n)=2 ω ( n ) = 2
一个集合为 {0,1,1} { 0 , 1 , 1 }
显然 {0,1,1} { 0 , 1 , 1 } 分别对应着 p1,p2,p3 p 1 , p 2 , p 3 是否选择了
如果我们干脆把 {0,1,1} { 0 , 1 , 1 } 视为 p1,p2,p3 p 1 , p 2 , p 3 的指数
得到一个数 p01+p02+p3 p 1 0 + p 2 0 + p 3
我们用上述方法得到的的数每个质因子的指数非0即1,也就是说没有平方因子
一提到没有平方因子,就可以想到
μ
μ
函数,实际上:
意义: n n 的没有平方因子()的约数的计数
回到我们一开始的问题:
之前我们说过:对于 2ω(n) 2 ω ( n ) 个集合,我们都可以让ta与 n n 的任何一个约数重组出的合法约数
枚举
μ
μ
:
于是利用整除分块,我们只要知道 μ2(i) μ 2 ( i ) 和 d(i) d ( i ) 的前缀和,预处理前 n23 n 2 3 项的情况下, O(n23) O ( n 2 3 ) 求出
前者就是
1−n
1
−
n
的无平方因子数个数,考虑容斥
O(n−−√)
O
(
n
)
后者就是套路
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
const int N=10000001;
ll n;
int sshu[N],tot=0;
ll mu[N],d[N],sm[N],sd[N],c[N];
bool no[N];
void prepare() {
mu[1]=1; d[1]=1;
for (int i=2;i<N;i++) {
if (!no[i]) {
sshu[++tot]=i;
mu[i]=-1;
d[i]=2; c[i]=1;
}
for (int j=1;j<=tot&&sshu[j]*i<N;j++) {
int v=sshu[j]*i;
no[v]=1; c[v]=1;
if (i%sshu[j]==0) {
c[v]=c[i]+1;
mu[v]=0; d[v]=d[i]*(c[v]+1)/c[v];
break;
}
mu[v]=-mu[i]; d[v]=d[i]<<1;
}
}
for (int i=1;i<N;i++)
sm[i]=sm[i-1]+mu[i]*mu[i],
sd[i]=sd[i-1]+d[i];
}
ll D(ll x) {
if (x<N) return (ll)sd[x];
ll ans=0,last;
for (ll i=1;i<=x;i=last+1) {
last=x/(x/i);
ans+=(x/i*(ll)(last-i+1));
}
return ans;
}
ll Mu(ll x) {
if (x<N) return sm[x];
ll ans=0,last;
for (ll i=1;i*i<=x;i++)
ans+=(mu[i]*(n/(i*i)));
return ans;
}
ll solve(ll n) {
ll ans=0,last;
for (ll i=1;i<=n;i=last+1) {
last=n/(n/i);
ans+=D(n/i)*(Mu(last)-Mu(i-1));
}
return ans;
}
int main()
{
int T;
prepare();
scanf("%d",&T);
while (T--) {
scanf("%lld",&n);
printf("%lld\n",solve(n));
}
return 0;
}