传送门:https://codeforces.com/gym/103428/problem/I
题目大意:给定一个数
n
n
n,问你
1
1
1到
n
n
n的整除关系的哈斯图上,相互两点的最短距离和是多少,定义两点
i
,
j
(
i
>
j
)
i,j(i>j)
i,j(i>j)之间的边权是
i
j
\frac{i}{j}
ji。
题解:如果不懂整除关系的哈斯图是什么样子可以看看下图。
不难发现,在图上的每一步行走,相当于乘上一个质数
p
p
p,或者除以一个质数
p
p
p。那么对于
i
=
p
i
1
s
i
1
p
i
2
s
i
2
⋯
p
i
r
s
i
r
,
j
=
p
j
1
s
j
1
p
j
2
s
j
2
⋯
p
j
r
s
j
r
i=p_{i1}^{s_{i1}}p_{i2}^{s_{i2}}\cdots p_{ir}^{s_{ir}},j=p_{j1}^{s_{j1}}p_{j2}^{s_{j2}}\cdots p_{jr}^{s_{jr}}
i=pi1si1pi2si2⋯pirsir,j=pj1sj1pj2sj2⋯pjrsjr,其两点的最短距离一定是
∑
p
∈
p
r
i
m
e
p
×
∣
s
i
−
s
j
∣
\sum\limits_{p\in prime}p\times \left|s_i-s_j\right|
p∈prime∑p×∣si−sj∣
稍加变换一下,可以得到
∑
p
∈
p
r
i
m
e
∑
i
=
1
p
i
≤
n
∑
j
=
0
i
−
1
2
p
(
i
−
j
)
×
CNT
n
(
p
i
)
×
CNT
n
(
p
j
)
\begin{aligned} &\sum\limits_{p\in prime}\sum\limits_{i=1}^{p^i\leq n}\sum\limits_{j=0}^{i-1}2p(i-j)\times \operatorname{CNT}_n(p^i)\times \operatorname{CNT}_n(p^j) \end{aligned}
p∈prime∑i=1∑pi≤nj=0∑i−12p(i−j)×CNTn(pi)×CNTn(pj)
其中定义
CNT
n
(
p
k
)
\operatorname{CNT}_n(p^k)
CNTn(pk)为
1
∼
n
1\sim n
1∼n中恰好被
p
k
p^k
pk整除且无法被
p
k
+
1
p^{k+1}
pk+1整除的数的个数。
考虑求
CNT
n
(
p
k
)
\operatorname{CNT}_n(p^k)
CNTn(pk),我们知道
⌊
n
p
k
⌋
\lfloor\frac{n}{p^k}\rfloor
⌊pkn⌋的意义是
1
∼
n
1\sim n
1∼n中至少可以被
p
k
p^k
pk整除的数的个数,类比
⌊
n
p
k
+
1
⌋
\lfloor\frac{n}{p^{k+1}}\rfloor
⌊pk+1n⌋的意义就是
1
∼
n
1\sim n
1∼n中至少可以被
p
k
+
1
p^{k+1}
pk+1整除的数的个数,用
⌊
n
p
k
⌋
\lfloor\frac{n}{p^k}\rfloor
⌊pkn⌋减去
⌊
n
p
k
+
1
⌋
\lfloor\frac{n}{p^{k+1}}\rfloor
⌊pk+1n⌋的话只会剩下只能被
p
k
p^k
pk整除的数的个数,即有
CNT
n
(
p
k
)
=
⌊
n
p
k
⌋
−
⌊
n
p
k
+
1
⌋
\operatorname{CNT}_n(p^k)=\lfloor\frac{n}{p^k}\rfloor-\lfloor\frac{n}{p^{k+1}}\rfloor
CNTn(pk)=⌊pkn⌋−⌊pk+1n⌋。
但是
n
n
n的范围在
1
∼
1
0
11
1\sim10^{11}
1∼1011,需要考虑更深一步的优化。不难发现对于每个
n
<
p
≤
n
,
p
∈
p
r
i
m
e
\sqrt n<p\le n,p\in prime
n<p≤n,p∈prime,其最高幂次只能为
1
1
1,对于它们的贡献,只有
2
p
×
CNT
n
(
p
)
×
CNT
n
(
1
)
=
2
p
⌊
n
p
⌋
(
n
−
⌊
n
p
⌋
)
2p\times \operatorname{CNT}_n(p)\times \operatorname{CNT}_n(1)=2p\lfloor\frac{n}{p}\rfloor(n-\lfloor\frac{n}{p}\rfloor)
2p×CNTn(p)×CNTn(1)=2p⌊pn⌋(n−⌊pn⌋)。假设我们已经求得每一个整除块内的全部质数和
S
⌊
n
i
⌋
\operatorname{S}_{\lfloor\frac{n}{i}\rfloor}
S⌊in⌋,就可以整除分块令答案加上
S
⌊
n
i
⌋
×
⌊
n
i
⌋
(
n
−
⌊
n
i
⌋
)
\operatorname{S}_{\lfloor\frac{n}{i}\rfloor}\times\lfloor\frac{n}{i}\rfloor(n-\lfloor\frac{n}{i}\rfloor)
S⌊in⌋×⌊in⌋(n−⌊in⌋)。刚好min25筛中的
g
(
⌊
n
i
⌋
)
g(\lfloor\frac{n}{i}\rfloor)
g(⌊in⌋)可以求得所有在
1
∼
⌊
n
i
⌋
1\sim\lfloor\frac{n}{i}\rfloor
1∼⌊in⌋中的质数和,只要两两相减便可以得到每一个大于
n
\sqrt n
n的整除块的全部质数和了。剩下的
n
log
n
\frac{\sqrt n}{\log n}
lognn个质数直接暴力按上式求即可,时间复杂度
O
(
n
0.75
log
n
+
n
log
n
×
log
2
n
)
=
O
(
n
0.75
log
n
)
\operatorname{O}(\frac{n^{0.75}}{\log n}+\frac{\sqrt n}{\log n}\times\log^2 n)=\operatorname{O}(\frac{n^{0.75}}{\log n})
O(lognn0.75+lognn×log2n)=O(lognn0.75)。
码量不大,就是卡常卡了很久(
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=6e5+5,M=1e9+7;
const ll inv2=M/2+1;
inline ll add(ll x,ll y){return x+y>=M?x+y-M:x+y;}
inline ll sub(ll x,ll y){return x-y<0?x-y+M:x-y;}
inline ll mul(ll x,ll y){return x*y%M;}
int cnt=0,prime[N],sum[N],g[N],id1[N],id2[N],tot=0;
bitset<N> vis;
ll n,sqn,v[N];
int get(ll x){
if(x>sqn) return id2[n/x];
return id1[x];
}
void pre(){
sqn=sqrt(n);
for(int i=2;i<=sqn;++i){
if(!vis[i]){
sum[i]=add(sum[i-1],i);
prime[++cnt]=i;
}
else sum[i]=sum[i-1];
for(int j=1;i*prime[j]<=sqn;++j){
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
for(ll l=1,r;l<=n;l=r+1,++tot){
ll t=n/l;
r=n/t;
g[tot]=sub(mul(mul((t+1)%M,t%M),inv2),1);
if(t>sqn) id2[r]=tot;
else id1[t]=tot;
v[tot]=t;
}
for(int i=1;i<=cnt;++i){
ll s=1ll*prime[i]*prime[i];
for(int j=0;s<=v[j];++j){
int id=get(v[j]/prime[i]);
g[j]=sub(g[j],mul(prime[i],sub(g[id],sum[prime[i-1]])));
}
}
}
signed main(){
cin>>n;
pre();
ll res=0,prev=g[get(sqn)];
for(int i=sqn;i;--i){
int id=get(n/i);
res+=mul(sub(g[id],prev),mul(i,n-i));
prev=g[id];
}
for(int i=1;i<=cnt;++i){
ll t=0;
for(ll j=1,a=prime[i];a<=n;++j,a*=prime[i]){
ll t1=0;
for(ll k=0,b=1;k<=j-1;++k,b*=prime[i])
t1+=mul(j-k,(n/b-n/b/prime[i])%M);
t+=mul((n/a-n/a/prime[i])%M,t1%M);
}
res+=mul(t%M,prime[i]);
}
cout<<mul(res,2)<<endl;
}