题目链接:洛谷 P2120 / LOJ #10189
题意
q q q 次询问,给定 n , m , a n,m,a n,m,a,求 ∑ i = 1 n ∑ j = 1 m [ σ 1 ( gcd ( i , j ) ≤ a ) ] σ 1 ( gcd ( i , j ) \sum\limits_{i=1}^n\sum\limits_{j=1}^m[\sigma_1(\gcd(i,j)\leq a)]\sigma_1(\gcd(i,j) i=1∑nj=1∑m[σ1(gcd(i,j)≤a)]σ1(gcd(i,j)。 n , m , a ≤ 1 0 5 n,m,a\leq 10^5 n,m,a≤105, q ≤ 2 × 1 0 4 q\leq 2\times 10^4 q≤2×104。
其中 σ 1 ( x ) \sigma_1(x) σ1(x) 指 x x x 的约数和。
题解
首先忽略 σ 0 ( gcd ( i , j ) ≤ a ) \sigma_0(\gcd(i,j)\leq a) σ0(gcd(i,j)≤a) 的限制,推式子( a / b = ⌊ a b ⌋ a/b=\lfloor\frac ab\rfloor a/b=⌊ba⌋):
∑ i = 1 n ∑ j = 1 m σ 1 ( gcd ( i , j ) ) = ∑ g = 1 min ( n , m ) σ 1 ( g ) ∑ i = 1 n / g ∑ j = 1 m / g [ gcd ( i , j ) = 1 ] = ∑ g = 1 min ( n , m ) σ 1 ( g ) ∑ d min ( n / g , m / g ) μ ( d ) ( m / d / g ) ( n / d / g ) = ∑ T = 1 min ( n , m ) ( m / T ) ( n / T ) ∑ g ∣ T σ 1 ( g ) μ ( T g ) \begin{aligned} &\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sigma_1(\gcd(i,j))\\ =&\sum\limits_{g=1}^{\min(n,m)}\sigma_1(g)\sum\limits_{i=1}^{n/g}\sum\limits_{j=1}^{m/g}[\gcd(i,j)=1]\\ =&\sum\limits_{g=1}^{\min(n,m)}\sigma_1(g)\sum\limits_{d}^{\min(n/g,m/g)}\mu(d)(m/d/g)(n/d/g)\\ =&\sum\limits_{T=1}^{\min(n,m)}(m/T)(n/T)\sum\limits_{g\mid T}\sigma_1(g)\mu({T\over g}) \end{aligned} ===i=1∑nj=1∑mσ1(gcd(i,j))g=1∑min(n,m)σ1(g)i=1∑n/gj=1∑m/g[gcd(i,j)=1]g=1∑min(n,m)σ1(g)d∑min(n/g,m/g)μ(d)(m/d/g)(n/d/g)T=1∑min(n,m)(m/T)(n/T)g∣T∑σ1(g)μ(gT)
假如没有限制,可以乱搞出 ∑ g ∣ T σ 1 ( g ) μ ( T g ) \sum\limits_{g\mid T}\sigma_1(g)\mu({T\over g}) g∣T∑σ1(g)μ(gT) 的前缀和,然后整除分块。
有限制时,考虑把询问按 a a a 排序,所有数按 σ 1 ( i ) \sigma_1(i) σ1(i) 排序。每次询问时,把 σ 1 ( i ) ≤ a \sigma_1(i)\leq a σ1(i)≤a 的 i i i 能够贡献到的 ∑ g ∣ T σ 1 ( g ) μ ( T g ) \sum\limits_{g\mid T}\sigma_1(g)\mu({T\over g}) g∣T∑σ1(g)μ(gT) 在树状数组上单点修改,整除分块时区间查询。
时间复杂度 O ( q n log n ) O(q\sqrt n \log n) O(qnlogn)。
代码:
/**********
Author: WLBKR5
Problem: loj 2193, luogu 3312
Name: 数表
Source: SDOI2014
Algorithm: 莫比乌斯反演, 树状数组
Date: 2020/06/04
Statue: accepted
Submission: loj.ac/submission/825281, www.luogu.com.cn/record/34142662
//The second submission seems to be faster
**********/
#include<bits/stdc++.h>
using namespace std;
#define uint unsigned int
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans;
}
const int N=1e5+10;
int& U(int &x,int y){x+=y;}
uint bit[N];
int lowbit(int x){return x&-x;}
uint query(int x){int ans=0;for(;x;x-=lowbit(x))ans+=bit[x];return ans;}
void modify(int x,uint val){if(val)for(;x<N;x+=lowbit(x))bit[x]+=val;}
struct Query{
int n,m,a;
int num;
uint ans;
};
bool cmp(const Query &a,const Query &b){ return a.a<b.a; }
bool pmc(const Query &a,const Query &b){ return a.num<b.num; }
Query q[N];
int sig[N],mu[N],pri[N],boo[N],cnt=0;
int a[N];
bool cmpi(int a,int b){
return sig[a]<sig[b];
}
int main(){
mu[1]=1;
for(int i=2;i<N;i++){
if(!boo[i])pri[cnt++]=i,mu[i]=-1;
//cerr<<"i "<<i<<" "<<mu[i]<<endl;
for(int j=0;j<cnt&&pri[j]*i<N;j++){
boo[pri[j]*i]=1;
if(i%pri[j]){
mu[i*pri[j]]=mu[i]*mu[pri[j]];
}else{
mu[i*pri[j]]=0;
break;
}
}
}
for(int i=1;i<N;i++)for(int j=i;j<N;j+=i)sig[j]+=i;
for(int i=1;i<N;i++)a[i]=i;
sort(a+1,a+N,cmpi);
int t=getint();
for(int i=0;i<t;i++){
q[i].n=getint();
q[i].m=getint();
q[i].a=getint();
q[i].num=i;
}
sort(q,q+t,cmp);
int p=1;
for(int i=0;i<t;i++){
while(sig[a[p]]<=q[i].a&&p<N){
for(int j=a[p];j<N;j+=a[p])modify(j,sig[a[p]]*mu[j/a[p]]);
++p;
}
uint m=q[i].m,n=q[i].n;
uint &ans=q[i].ans;
uint lastq=0;
for(uint l=1,r=0;l<=min(m,n);l=r+1){
r=min(n/(n/l),m/(m/l));
uint _=query(r);
ans+=(m/l)*(n/l)*(_-lastq);
lastq=_;
}
}
sort(q,q+t,pmc);
for(int i=0;i<t;i++){
printf("%d\n",int(q[i].ans&((1u<<31)-1)));
}
return 0;
}