前置知识:莫比乌斯反演
题解
不妨设
n
≤
m
n\leq m
n≤m。
若没有
a
a
a的限制,则题意即求
∑ i = 1 n ∑ j = 1 n σ ( gcd ( i , j ) ) \sum\limits_{i=1}^n\sum\limits_{j=1}^n\sigma(\gcd(i,j)) i=1∑nj=1∑nσ(gcd(i,j))
枚举 gcd \gcd gcd可得
∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ σ ( d ) [ gcd ( i , j ) = 1 ] \sum\limits_{d=1}^n\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}\sigma(d)[\gcd(i,j)=1] d=1∑ni=1∑⌊dn⌋j=1∑⌊dm⌋σ(d)[gcd(i,j)=1]
将 σ ( d ) \sigma(d) σ(d)移到前面,利用莫比乌斯函数的性质,可得
∑ d = 1 n σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ i , k ∣ j μ ( k ) \sum\limits_{d=1}^n\sigma(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}\sum\limits_{k|i,k|j}\mu(k) d=1∑nσ(d)i=1∑⌊dn⌋j=1∑⌊dm⌋k∣i,k∣j∑μ(k)
稍作整理可得
∑ d = 1 n σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ i , k ∣ j μ ( k ) \sum\limits_{d=1}^n\sigma(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}\sum\limits_{k|i,k|j}\mu(k) d=1∑nσ(d)i=1∑⌊dn⌋j=1∑⌊dm⌋k∣i,k∣j∑μ(k)
枚举 k k k,可得
∑ d = 1 n σ ( d ) ∑ k = 1 ⌊ n d ⌋ ∑ i = 1 ⌊ n k d ⌋ ∑ j = 1 ⌊ m k d ⌋ μ ( k ) \sum\limits_{d=1}^n\sigma(d)\sum\limits_{k=1}^{\lfloor\frac nd\rfloor}\sum\limits_{i=1}^{\lfloor\frac{n}{kd}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{kd}\rfloor}\mu(k) d=1∑nσ(d)k=1∑⌊dn⌋i=1∑⌊kdn⌋j=1∑⌊kdm⌋μ(k)
即可得
∑ d = 1 n σ ( d ) ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⌊ n k d ⌋ ⌊ m k d ⌋ \sum\limits_{d=1}^n\sigma(d)\sum\limits_{k=1}^{\lfloor\frac nd\rfloor}\mu(k)\lfloor\dfrac{n}{kd}\rfloor\lfloor\dfrac{m}{kd}\rfloor d=1∑nσ(d)k=1∑⌊dn⌋μ(k)⌊kdn⌋⌊kdm⌋
令 T = k d T=kd T=kd,得
∑ d = 1 n σ ( d ) ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⌊ n T ⌋ ⌊ m T ⌋ \sum\limits_{d=1}^n\sigma(d)\sum\limits_{k=1}^{\lfloor\frac nd\rfloor}\mu(k)\lfloor\dfrac nT\rfloor\lfloor\dfrac mT\rfloor d=1∑nσ(d)k=1∑⌊dn⌋μ(k)⌊Tn⌋⌊Tm⌋
枚举 T T T可得
∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T σ ( d ) μ ( T d ) \sum\limits_{T=1}^n\lfloor\dfrac nT\rfloor\lfloor\dfrac mT\rfloor\sum\limits_{d|T}\sigma(d)\mu(\dfrac Td) T=1∑n⌊Tn⌋⌊Tm⌋d∣T∑σ(d)μ(dT)
如果没有 a a a的限制,则只需求出 ∑ d ∣ T σ ( d ) μ ( T d ) \sum\limits_{d|T}\sigma(d)\mu(\dfrac Td) d∣T∑σ(d)μ(dT)的前缀和,再用数论分块就可以了。
设 f ( T ) = ∑ d ∣ T σ ( d ) μ ( T d ) f(T)=\sum\limits_{d|T}\sigma(d)\mu(\dfrac Td) f(T)=d∣T∑σ(d)μ(dT),我们发现只有当 σ ( d ) ≤ a \sigma(d)\leq a σ(d)≤a时, σ ( d ) × μ ( T d ) \sigma(d)\times \mu(\dfrac Td) σ(d)×μ(dT)才会对 f ( T ) f(T) f(T)有贡献。
我们可以按 σ ( d ) \sigma(d) σ(d)从小到大来对 d d d排序,对询问按 a a a从小到大排序。随着 a a a的增大,会有一些 σ ( d ) × μ ( T d ) \sigma(d)\times \mu(\dfrac Td) σ(d)×μ(dT)对 f ( T ) f(T) f(T)从无贡献变为有贡献。可以用树状数组维护 f ( T ) f(T) f(T),然后动态修改。原来的式子用数论分块来求,求 f ( T ) f(T) f(T)的前缀和可以用区间查询来实现。
更新 f ( T ) f(T) f(T)最多需要 ∑ i = 1 n n i ≈ n ln n \sum\limits_{i=1}^n\dfrac ni \approx n\ln n i=1∑nin≈nlnn次,每次更新 f ( T ) f(T) f(T)的时间复杂度为 O ( log n ) O(\log n) O(logn),则更新的时间复杂度为 O ( n log 2 n ) O(n\log^2 n) O(nlog2n)。每次处理询问的时间复杂度为 O ( n log n ) O(\sqrt n\log n) O(nlogn),则处理询问的时间复杂度为 O ( q n log n ) O(q\sqrt n\log n) O(qnlogn)。总时间复杂度为 O ( q n log n + n log 2 n ) O(q\sqrt n\log n+n\log^2 n) O(qnlogn+nlog2n)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
const long long mod=2147483648ll;
int t,z[100005],p[100005],mu[100005],ans[20005],tr[100005];
long long x;
struct node1{
int x,id;
}g[100005];
struct node2{
int n,m,a,id;
}w[20005];
bool cmp1(node1 ax,node1 bx){
return ax.x<bx.x;
}
bool cmp2(node2 ax,node2 bx){
return ax.a<bx.a;
}
int lb(int i){
return i&(-i);
}
void pt(int i,int t){
while(i<=N){
tr[i]+=t;i+=lb(i);
}
}
int gt(int i){
int re=0;
while(i){
re+=tr[i];i-=lb(i);
}
return re;
}
int sum(int n,int m){
int re=0;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
re+=(gt(r)-gt(l-1))*(n/l)*(m/l);
}
return re;
}
int main()
{
mu[1]=1;
for(int i=2;i<=N;i++){
if(!z[i]){
p[++p[0]]=i;mu[i]=-1;
}
for(int j=1;j<=p[0]&&i*p[j]<=N;j++){
z[i*p[j]]=1;
if(i%p[j]==0){
mu[i*p[j]]=0;
break;
}
mu[i*p[j]]=-mu[i];
}
}
for(int i=1;i<=N;i++){
for(int j=i;j<=N;j+=i) g[j].x+=i;
}
for(int i=1;i<=N;i++) g[i].id=i;
sort(g+1,g+N+1,cmp1);
scanf("%d",&t);
for(int i=1;i<=t;i++){
scanf("%d%d%d",&w[i].n,&w[i].m,&w[i].a);w[i].id=i;
if(w[i].n>w[i].m) swap(w[i].n,w[i].m);
}
sort(w+1,w+t+1,cmp2);
for(int i=1,j=0;i<=t;i++){
while(w[i].a>=g[j+1].x&&j+1<=N){
++j;
for(int k=g[j].id;k<=N;k+=g[j].id){
pt(k,g[j].x*mu[k/g[j].id]);
}
}
ans[w[i].id]=sum(w[i].n,w[i].m);
}
for(int i=1;i<=t;i++){
ans[i]=(1ll*ans[i]+mod)%mod;
printf("%lld\n",ans[i]);
}
return 0;
}
文章讲述了如何使用莫比乌斯反演解决洛谷P3312题目中的数表问题,涉及到数学和算法的知识,包括数论分块、前缀和以及树状数组的动态维护,主要讨论了如何在限制条件a下优化计算过程,以达到较高的时间复杂度效率。
369

被折叠的 条评论
为什么被折叠?



