题目链接
https://lydsy.com/JudgeOnline/problem.php?id=3529
题解
题目要求
∑
i
=
1
n
∑
j
=
1
m
σ
0
(
gcd
(
i
,
j
)
)
[
σ
0
(
gcd
(
i
,
j
)
)
≤
a
]
\sum_{i=1}^n\sum_{j=1}^m \sigma_0(\gcd(i,j))[\sigma_0(\gcd(i,j))\leq a]
i=1∑nj=1∑mσ0(gcd(i,j))[σ0(gcd(i,j))≤a]
稍微转化一下就是
∑
T
=
1
min
⌊
n
T
⌋
⌊
m
T
⌋
∑
k
∣
T
μ
(
T
k
)
σ
0
(
k
)
[
σ
0
(
k
)
≤
a
]
\sum_{T=1}^{\min} \lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{k|T}\mu(\frac{T}{k})\sigma_0(k)[\sigma_0(k)\leq a]
T=1∑min⌊Tn⌋⌊Tm⌋k∣T∑μ(kT)σ0(k)[σ0(k)≤a]
设
f
(
T
)
=
∑
k
∣
T
μ
(
T
k
)
σ
0
(
k
)
[
σ
0
(
k
)
≤
a
]
f(T)=\sum_{k|T}\mu(\frac{T}{k})\sigma_0(k)[\sigma_0(k)\leq a]
f(T)=k∣T∑μ(kT)σ0(k)[σ0(k)≤a]
考虑对于每一个
σ
0
(
k
)
=
a
\sigma_0(k)=a
σ0(k)=a,更新其在对应位置上的
f
f
f,可以预处理出
σ
0
\sigma_0
σ0,然后以权值为关键字sort一边,离线处理询问,将询问以
a
a
a为关键字sort一遍,用树状数组维护每个位置的
f
f
f。
这里有一个小技巧,由于对 2 31 2^{31} 231取模,因此可以利用自然溢出,用unsigned来记录答案,最后取模即可。
时间复杂度 O ( T n log n + n log 2 n ) O(T\sqrt{n}\log n+n\log^2 n) O(Tnlogn+nlog2n)。
代码
#include <cstdio>
#include <algorithm>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int maxn=100000;
const int maxq=20000;
const unsigned mod=1<<31;
namespace bit
{
unsigned val[maxn+10];
int lowbit(int x)
{
return x&(-x);
}
int add(int pos,unsigned v)
{
while(pos<=maxn)
{
val[pos]+=v;
pos+=lowbit(pos);
}
return 0;
}
unsigned getsum(int pos)
{
unsigned res=0;
while(pos)
{
res+=val[pos];
pos-=lowbit(pos);
}
return res;
}
}
struct data
{
int id;
unsigned val;
bool operator <(const data &other) const
{
return val<other.val;
}
};
int p[maxn+10],prime[maxn+10],cnt,low[maxn+10],num[maxn+10];
unsigned mu[maxn+10];
data d[maxn+10];
unsigned F(int x,int y)
{
unsigned res=1;
for(int i=1; i<=y+1; ++i)
{
res*=x;
}
return (res-1)/(x-1);
}
int getprime()
{
for(int i=1; i<=maxn; ++i)
{
d[i].id=i;
}
p[1]=mu[1]=num[1]=d[1].val=1;
low[1]=0;
for(int i=2; i<=maxn; ++i)
{
if(!p[i])
{
prime[++cnt]=i;
low[i]=num[i]=1;
mu[i]=-1;
d[i].val=i+1;
}
for(int j=1; (j<=cnt)&&(i*prime[j]<=maxn); ++j)
{
int x=i*prime[j];
p[x]=1;
if(i%prime[j]==0)
{
low[x]=low[i]+1;
num[x]=num[i];
mu[x]=0;
d[x].val=d[num[x]].val*F(prime[j],low[x]);
break;
}
low[x]=1;
num[x]=i;
mu[x]=-mu[i];
d[x].val=d[i].val*(prime[j]+1);
}
}
std::sort(d+1,d+maxn+1);
return 0;
}
struct query
{
int n,m,v,id;
bool operator <(const query &other) const
{
return v<other.v;
}
};
int T;
query q[maxq+10];
unsigned ans[maxn+10];
int main()
{
getprime();
T=read();
for(int i=1; i<=T; ++i)
{
q[i].n=read();
q[i].m=read();
q[i].v=read();
q[i].id=i;
}
std::sort(q+1,q+T+1);
for(int i=1,j=1; i<=T; ++i)
{
while((j<=maxn)&&(d[j].val<=(unsigned)q[i].v))
{
for(int k=1; k<=maxn/d[j].id; ++k)
{
bit::add(d[j].id*k,mu[k]*d[j].val);
}
++j;
}
for(int l=1,r; l<=std::min(q[i].n,q[i].m); l=r+1)
{
r=std::min(q[i].n/(q[i].n/l),q[i].m/(q[i].m/l));
ans[q[i].id]+=(bit::getsum(r)-bit::getsum(l-1))*(q[i].n/l)*(q[i].m/l);
}
}
for(int i=1; i<=T; ++i)
{
printf("%u\n",ans[i]&(mod-1));
}
return 0;
}