题目分析
首先,我们忽略掉
a
a
这个条件,设,那么我们要求的就是:
∑i=1n∑j=1m∑d|gcd(i,j)d
∑
i
=
1
n
∑
j
=
1
m
∑
d
|
g
c
d
(
i
,
j
)
d
由于此题跟gcd有关,所以我们猜测这道题要用莫比乌斯反演,所以不管三七二十一把莫比乌斯反演的常见应用列上来:
设 g(k)=∑ni=1∑mj=1[gcd(i,j)==k] g ( k ) = ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) == k ]
则 g(k)=∑k|dμ(dk)⌊nd⌋⌊md⌋ g ( k ) = ∑ k | d μ ( d k ) ⌊ n d ⌋ ⌊ m d ⌋
设 F(x) F ( x ) 为 x x 的约数和,则答案就被转化为了
即 ∑xx=1F(x)∑x|dμ(dx)⌊nd⌋⌊md⌋ ∑ x = 1 x F ( x ) ∑ x | d μ ( d x ) ⌊ n d ⌋ ⌊ m d ⌋
那么就是 ∑nd⌊nd⌋⌊md⌋∑x|dF(x)μ(dx) ∑ d n ⌊ n d ⌋ ⌊ m d ⌋ ∑ x | d F ( x ) μ ( d x )
喔~如果是求这个的花,我们可以首先枚举约数,求出 F(x) F ( x ) ,然后打表 ∑x|dF(x)μ(dx) ∑ x | d F ( x ) μ ( d x ) 的前缀和,然后用一个常用分块优化处理前面的 ∑nd⌊nd⌋⌊md⌋ ∑ d n ⌊ n d ⌋ ⌊ m d ⌋ (详见 莫比乌斯反演)
可是,只有小于等于 a a 的才会有贡献,怎么办?
离线!将 F(x) F ( x ) 从小到大排序,将询问按照 a a 从小到大排序,然后使用一个树状数组,动态往树状数组里加,前缀和就求树状数组前缀和即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int Q,n,m,lim,tot,now;
struct question{int n,m,a,id;}q[20005];
struct hans{int v,id;}F[N];
int mu[N],is[N],pri[N],tr[N],ans[20005];
void init() {
for(int i=1;i<=lim;++i)//预处理F
for(int j=i;j<=lim;j+=i) F[j].v+=i;
for(int i=1;i<=lim;++i) F[i].id=i;
mu[1]=1;
for(int i=2;i<=lim;++i) {//预处理莫比乌斯函数
if(!is[i]) pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&pri[j]*i<=lim;++j) {
is[i*pri[j]]=1;
if(i%pri[j]==0) {mu[i*pri[j]]=0;break;}
else mu[i*pri[j]]=-mu[i];
}
}
}
int cmp1(question x,question y) {return x.a<y.a;}
int cmp2(hans x,hans y) {return x.v<y.v;}
#define lowbit(x) (x&(-x))
void add(int x,int num) {while(x<=lim) tr[x]+=num,x+=lowbit(x);}
int query(int x) {
int re=0;
while(x) re+=tr[x],x-=lowbit(x);
return re;
}
void getans(int x) {
for(int i=1,j;i<=q[x].n;i=j+1) {//常用分块优化
j=min(q[x].n/(q[x].n/i),q[x].m/(q[x].m/i));
ans[q[x].id]+=(q[x].n/i)*(q[x].m/i)*(query(j)-query(i-1));
}
}
int main()
{
scanf("%d",&Q);
for(int i=1;i<=Q;++i) {
scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a);
q[i].id=i;if(q[i].n>q[i].m) swap(q[i].n,q[i].m);
lim=max(lim,q[i].n);
}
init();
sort(q+1,q+1+Q,cmp1),sort(F+1,F+1+lim,cmp2);
now=1;
for(int i=1;i<=Q;++i) {
while(now<=lim&&F[now].v<=q[i].a) {
for(int j=F[now].id;j<=lim;j+=F[now].id)
add(j,F[now].v*mu[j/F[now].id]);
++now;
}
getans(i);
}
for(int i=1;i<=Q;++i) printf("%d\n",ans[i]&0x7fffffff);
return 0;
}