题目大意:
求
∑i=1n∑j=1mσ(gcd(i,j))[σ(gcd(i,j))≤a]
其中
σ(x)
表示
x
的约数和。
先假设没有
那么就是求
∑i=1n∑j=1mσ(gcd(i,j))
∑i=1n∑j=1m∑d=1nσ(d)[d=gcd(i,j)]
∑d=1nσ(d)∑i=1⌊nd⌋∑j=1⌊md⌋[gcd(i,j)=1]
∑d=1nσ(d)∑i=1⌊nd⌋∑j=1⌊md⌋∑t|i,t|jμ(t)
∑d=1nσ(d)∑t=1⌊nd⌋μ(t)⌊ndt⌋⌊mdt⌋
令 T=dt
∑T=1n⌊nT⌋⌊mT⌋∑d|Tσ(d)μ(Td)
令 f(x)=∑d|xσ(d)μ(xd)
∑T=1n⌊nT⌋⌊mT⌋f(T)
那么
O(nlogn)
求出
f
,就能
然后考虑有
a
的限制,那么
离线,将询问按照
a
从小到大排序,每次用
时间复杂度
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define M 2147483648
#define N 100010
struct Qu{
int x,y,z,f;
}q[20010];
struct Node{
int w,f;
}a[N];
ll c[N],f[N],Ans[N];
int Q,i,j,k,n,m,p[N],Top,mu[N],Last;
bool b[N];
inline bool Cmp2(Node a,Node b){
return a.w<b.w;
}
inline bool Cmp(Qu a,Qu b){
return a.z<b.z;
}
inline void Init(){
mu[1]=1;
for(i=2;i<N;i++){
if(!b[i]){
p[++Top]=i;
mu[i]=-1;
}
for(j=1;j<=Top&&p[j]*i<N;j++){
b[p[j]*i]=1;
if(!(i%p[j])){mu[p[j]*i]=0;break;}
mu[p[j]*i]=-mu[i];
}
}
for(i=1;i<N;i++)
for(j=1;j*i<N;j++)
f[i*j]+=i;
for(i=1;i<N;i++)a[i].w=f[i],a[i].f=i;
sort(a+1,a+N,Cmp2);
}
inline void Update(int x,ll y){
for(;x<N;x+=x&-x)c[x]=(c[x]+y)%M;
}
inline ll Query(int x){
ll Ans=0;
for(;x;x-=x&-x)Ans+=c[x];
return Ans;
}
inline void Update1(int x){
for(int i=1;i*x<N;i++)Update(i*x,f[x]*mu[i]);
}
inline int Min(int x,int y){
return x<y?x:y;
}
inline ll GetAns(int n,int m){
ll Ans=0;
for(int i=1;i<=n;){
int j=Min(n/(n/i),m/(m/i));
Ans=(Ans+(Query(j)-Query(i-1))*(n/i)%M*(m/i)%M)%M;
i=j+1;
}
return Ans;
}
int main(){
scanf("%d",&Q);
for(i=1;i<=Q;i++){
scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);
if(q[i].x>q[i].y)swap(q[i].x,q[i].y);
q[i].f=i;
}
Init();
sort(q+1,q+Q+1,Cmp);
for(i=1;i<=Q;i++){
while(Last<N-1&&a[Last+1].w<=q[i].z)Update1(a[++Last].f);
Ans[q[i].f]=GetAns(q[i].x,q[i].y);
}
for(i=1;i<=Q;i++)printf("%lld\n",(Ans[i]+M)%M);
return 0;
}