假设$n\leq m$,我们先不考虑$\leq a$的限制
$\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sigma((i,j))=\sum\limits_{T=1}^n\left\lfloor\frac nT\right\rfloor\left\lfloor\frac mT\right\rfloor\sum\limits_{d|T}\sigma(d)\mu\left(\frac Td\right)$
我们可以线性筛出$g(n)=\sum\limits_{d|n}\mu(d)\mu\left(\frac nd\right)$并$O(\sqrt n)$回答询问
现在加上了$\leq a$的限制,其实就是只计算$\sigma(d)\leq a$的$d$
考虑离线,把所有询问按$a$排序,按$\sigma(d)$从小到大更新$g$(更新$d$的倍数处的$g$)就可以做了
用树状数组维护$g$,时间复杂度$O\left(n\log_2^2n+q\sqrt n\log_2n\right)$
#include<stdio.h> #include<algorithm> using namespace std; typedef long long ll; const int T=100000; int pr[T+10],mu[T+10],d[T+10],sd[T+10]; bool np[T+10]; void sieve(){ int i,j,M; M=0; mu[1]=1; d[1]=1; sd[1]=1; for(i=2;i<=T;i++){ if(!np[i]){ pr[++M]=i; mu[i]=-1; d[i]=i; sd[i]=i+1; } for(j=1;j<=M&&i*pr[j]<=T;j++){ np[i*pr[j]]=1; if(i%pr[j]==0){ d[i*pr[j]]=d[i]*pr[j]; sd[i*pr[j]]=sd[i/d[i]]*(sd[d[i]]*pr[j]+1); break; } mu[i*pr[j]]=-mu[i]; d[i*pr[j]]=pr[j]; sd[i*pr[j]]=sd[i]*sd[pr[j]]; } } } int s[100010]; int lowbit(int x){return x&-x;} void add(int x,int d){ while(x<=T){ s[x]+=d; x+=lowbit(x); } } int query(int x){ int r=0; while(x){ r+=s[x]; x-=lowbit(x); } return r; } struct ask{ int n,m,a,i; }q[100010]; bool operator<(ask a,ask b){return a.a<b.a;} int p[100010],ans[100010]; bool cmp(int a,int b){return sd[a]<sd[b];} #define qn q[i].n #define qm q[i].m int main(){ sieve(); int m,M,i,j,nex,s; scanf("%d",&m); for(i=1;i<=m;i++){ scanf("%d%d%d",&qn,&qm,&q[i].a); if(qn>qm)swap(qn,qm); q[i].i=i; } sort(q+1,q+m+1); for(i=1;i<=T;i++)p[i]=i; sort(p+1,p+T+1,cmp); M=1; for(i=1;i<=m;i++){ while(M<=T&&sd[p[M]]<=q[i].a){ for(j=p[M];j<=T;j+=p[M]){ if(mu[j/p[M]])add(j,sd[p[M]]*mu[j/p[M]]); } M++; } s=0; for(j=1;j<=qn;j=nex+1){ nex=min(qn/(qn/j),qm/(qm/j)); s+=(qn/j)*(qm/j)*(query(nex)-query(j-1)); } ans[q[i].i]=s; } for(i=1;i<=m;i++)printf("%d\n",ans[i]&2147483647); }