数表
题解
挺简单的一道题。
题目可以简化为
∑
i
∈
[
1
,
n
]
∑
j
∈
[
1
,
m
]
g
[
(
i
,
j
)
]
[
g
[
(
i
,
j
)
]
≤
a
]
\sum_{i\in [1,n]}\sum_{j\in[1,m]}g[(i,j)][g[(i,j)]\leq a]
∑i∈[1,n]∑j∈[1,m]g[(i,j)][g[(i,j)]≤a]。其中,
g
(
x
)
g(x)
g(x)表示
x
x
x的约数和。虽然原题也够简洁了
我们还是先推一下式子。
令
f
(
x
)
=
∑
i
∈
[
1
,
n
]
∑
j
∈
[
1
,
m
]
[
(
i
,
j
)
=
=
x
]
f(x)=\sum_{i\in[1,n]}\sum_{j\in[1,m]}[(i,j)==x]
f(x)=∑i∈[1,n]∑j∈[1,m][(i,j)==x]。
显然,原式可化为
∑
i
∈
[
1
,
min
(
n
,
m
)
]
g
(
i
)
f
(
i
)
[
g
(
i
)
≤
a
]
\sum_{i\in [1,\min(n,m)]}g(i)f(i)[g(i)\leq a]
∑i∈[1,min(n,m)]g(i)f(i)[g(i)≤a]。
但很明显,这个
f
(
x
)
f(x)
f(x)是很难直接求出来的,考虑莫比乌斯反演。
令
F
(
x
)
=
∑
i
∈
[
1
,
n
]
∑
j
∈
[
1
,
m
]
[
x
∣
(
i
,
j
)
]
F(x)=\sum_{i\in[1,n]}\sum_{j\in[1,m]}[x|(i,j)]
F(x)=∑i∈[1,n]∑j∈[1,m][x∣(i,j)]。
此时有,
F
(
x
)
=
∑
d
∣
x
f
(
d
)
=
⌊
n
x
⌋
⌊
m
x
⌋
F(x)=\sum_{d|x}f(d)=\left\lfloor \frac{n}{x} \right\rfloor\left\lfloor \frac{m}{x} \right\rfloor
F(x)=∑d∣xf(d)=⌊xn⌋⌊xm⌋。
于是,
f
(
x
)
=
∑
x
∣
d
μ
(
d
x
)
F
(
d
)
=
∑
x
∣
d
μ
(
d
x
)
⌊
n
d
⌋
⌊
m
d
⌋
f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)=\sum_{x|d}\mu(\frac{d}{x})\left\lfloor \frac{n}{d} \right\rfloor\left\lfloor \frac{m}{d} \right\rfloor
f(x)=∑x∣dμ(xd)F(d)=∑x∣dμ(xd)⌊dn⌋⌊dm⌋。
代入原式,
=
∑
i
∈
[
1
,
min
(
n
,
m
)
]
g
(
i
)
∑
i
∣
d
μ
(
d
i
)
⌊
n
d
⌋
⌊
m
d
⌋
[
g
(
i
)
≤
a
]
=\sum_{i\in [1,\min(n,m)]}g(i)\sum_{i|d}\mu(\frac{d}{i})\left\lfloor \frac{n}{d} \right\rfloor\left\lfloor \frac{m}{d} \right\rfloor[g(i)\leq a]
=∑i∈[1,min(n,m)]g(i)∑i∣dμ(id)⌊dn⌋⌊dm⌋[g(i)≤a]
=
∑
d
∈
[
1
,
min
(
n
,
m
)
]
⌊
n
d
⌋
⌊
m
d
⌋
∑
i
∣
d
[
g
(
i
)
≤
a
]
g
(
i
)
μ
(
d
i
)
=\sum_{d\in[1,\min(n,m)]}\left\lfloor \frac{n}{d} \right\rfloor\left\lfloor \frac{m}{d} \right\rfloor\sum_{i|d}[g(i)\leq a]g(i)\mu (\frac{d}{i})
=∑d∈[1,min(n,m)]⌊dn⌋⌊dm⌋∑i∣d[g(i)≤a]g(i)μ(id)
很明显,只有当后面的
[
g
(
i
)
≤
a
]
[g(i)\leq a]
[g(i)≤a]成立时,后面式子的值才会不为0。
于是,我们考虑将询问离线下来,通过
a
a
a值的大小排序,将每个
g
(
i
)
g(i)
g(i)依次加入。
我们可以通过树状数组来维护后面的一段的
g
(
i
)
g(i)
g(i),每次当
a
a
a值扩大时再将
g
(
i
)
g(i)
g(i)插入进去。
至于
g
(
i
)
g(i)
g(i)的值,我们可以在最开始预处理莫比乌斯函数时一块求出。
当然,
O
(
(
q
+
n
)
n
)
O\left((q+n)n\right)
O((q+n)n)的时间复杂度时明显过不了的,考虑数论分块。
因为数论分块涉及到区间和的动态维护,我们需要通过树状数组之类的数据结构来维护每个项的值。
当插入
g
(
i
)
g(i)
g(i)的时候会影响到
n
i
\frac{n}{i}
in个项的值,总共会有
n
l
n
n
nln\, n
nlnn次插入操作。
总时间复杂度
O
(
n
log
n
ln
n
+
q
n
l
o
g
n
)
O\left(n\log\, n\ln\,n+q\sqrt{n}log\, n\right)
O(nlognlnn+qnlogn),可以过题。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define MAXN 100010
#define lowbit(x) (x&-x)
typedef long long LL;
const int INF=0x7f7f7f7f;
const LL mo=(1<<31);
typedef pair<int,int> pii;
#define gc() getchar()
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=gc();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
x*=f;
}
int mobius[MAXN],prime[MAXN],cntp,q,tot;
LL ans[MAXN],tr[MAXN],g[MAXN];
bool oula[MAXN];
struct ming{int n,m,a,id;}s[MAXN];
struct number{LL d;int id;}f[MAXN];
bool cmp(ming x,ming y){return x.a<y.a;}
bool cmp1(number x,number y){return x.d<y.d;}
void init(){
mobius[1]=1;f[1].d=f[1].id=1;
for(int i=2;i<=1e5;i++){
f[i].id=i;if(!oula[i])prime[++cntp]=i,mobius[i]=-1,g[i]=i+1,f[i].d=i+1;
for(int j=1;j<=cntp&&i*prime[j]<=1e5;j++){
oula[i*prime[j]]=1;
if(i%prime[j]==0){
g[i*prime[j]]=1ll*g[i]*prime[j]+1LL;
f[i*prime[j]].d=1ll*f[i].d/g[i]*g[i*prime[j]];
mobius[i*prime[j]]=0;break;
}
mobius[i*prime[j]]=-mobius[i];
f[i*prime[j]].d=1ll*f[i].d*f[prime[j]].d;
g[i*prime[j]]=1ll*prime[j]+1LL;
}
}
}
void insert(int x,LL w){while(x<=tot)tr[x]+=w,x+=lowbit(x);}
LL query(int x){LL res=0;while(x)res+=tr[x],x-=lowbit(x);return res;}
signed main(){
read(q);init();
for(int i=1;i<=q;i++)
read(s[i].n),read(s[i].m),read(s[i].a),
s[i].id=i,tot=max(max(s[i].m,s[i].n),tot);
sort(s+1,s+q+1,cmp);sort(f+1,f+tot+1,cmp1);int j=1;
for(int i=1;i<=q;i++){
while(j<=tot&&f[j].d<=s[i].a){
for(int k=f[j].id;k<=tot;k+=f[j].id)
insert(k,f[j].d*mobius[k/f[j].id]);
j++;
}
for(int l=1,r;l<=min(s[i].n,s[i].m);l=r+1)
r=min(s[i].n/(s[i].n/l),s[i].m/(s[i].m/l)),
ans[s[i].id]+=1ll*(query(r)-query(l-1))*(s[i].n/l)*(s[i].m/l);
}
for(int i=1;i<=q;i++)printf("%lld\n",ans[i]&(~mo));
return 0;
}