不得已来写一下这道题的博客,,这道题很有思想代表性。
我们设也就是约数和。且令n<=m。
则目标是
我们设即gcd为x的数对个数和。
如果我们忽略a的限制,则目标是
我们令,因为这是一个狄利克雷卷积形式,可以nlogn筛出来。
则原来的
则f(d)可以求前缀和,然后整除分块之类地。
但最重要的!有a的限制。
我们的处理方式是将询问离线按照a排序,对于所有g(i)排序,原来的做法是选择数字枚举约数,我们枚举约数用树状数组计算其对后面所有数的贡献。
一个一个加就行,代码好好看
#include<bits/stdc++.h>
#define in read()
using namespace std;
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();
if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;
ch=getchar();
}
return cnt*f;
}
int mu[100003],F[100003];
int prime[100003],cnt,vis[100003];
void prepare(){
mu[1]=1;vis[1]=1;
for(int i=2;i<=100003;i++){
if(!vis[i])prime[++cnt]=i,mu[i]=-1;
for(int j=1;prime[j]*i<=100003&&j<=cnt;j++){
vis[prime[j]*i]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[prime[j]*i]=-mu[i];
}
}
for(int i=1;i<=100003;i++){
for(int j=i;j<=100003;j+=i){
F[j]+=i;
}
}
}
struct quee{
int n,m,a,id;
}que[100003];
struct node{
int id,g;
}A[100003];
struct tree{
int sum;
}t[100003];
bool cmp1(quee a,quee b){
return a.a<b.a;
}
bool cmp2(node a,node b){
return a.g<b.g;
}
int lowbit(int u){
return u&(-u);
}
void add(int u,int key){
while(u<=100003){
t[u].sum+=key;
u+=lowbit(u);
}
}
int query(int u){
int ans=0;
while(u){
ans+=t[u].sum;
u-=lowbit(u);
}
return ans;
}
int calc(int nn,int mm){
int ret=0;if(nn>mm)swap(nn,mm);
for(int l=1,r;l<=min(nn,mm);l=r+1){
r=min(nn/(nn/l),mm/(mm/l));
ret+=(nn/l)*(mm/l)*(query(r)-query(l-1));
}
return ret;
}
int main(){
prepare();
// for(int i=1;i<=10;i++){
// cout<<mu[i]<<" ";
// }cout<<endl;
unsigned int Ans[100003];
int q=in;
for(int i=1;i<=q;i++){
que[i].n=in;que[i].m=in;que[i].a=in;que[i].id=i;
}
sort(que+1,que+q+1,cmp1);
for(int i=1;i<=100003;i++){
A[i].g=F[i];A[i].id=i;
}
sort(A+1,A+100003+1,cmp2);
int tot=0;
for(int i=1;i<=q;i++){
while(tot<=100003&&A[tot+1].g<=que[i].a){
++tot;
for(int j=1;j*A[tot].id<=100003;j++){
add(j*A[tot].id,mu[j]*A[tot].g);
}
}
Ans[que[i].id]=calc(que[i].n,que[i].m);
}
for(int i=1;i<=q;i++){
printf("%d\n",Ans[i]&2147483647);
}
return 0;
}