一、题目
二、解法
首先来看一个简化的问题, a = 1 a=1 a=1 和 c = 1 c=1 c=1的情况,我们先把范围都除以 k k k,问题就变成求互质数对的个数,考虑莫比乌斯反演。
设 f ( i ) f(i) f(i) 为 gcd ( x , y ) = i \gcd(x,y)=i gcd(x,y)=i 且 1 ≤ x ≤ b , 1 ≤ y ≤ d 1\leq x\leq b,1\leq y\leq d 1≤x≤b,1≤y≤d 满足条件的对数。
设 F ( i ) F(i) F(i) 为 i ∣ gcd ( x , y ) i|\gcd(x,y) i∣gcd(x,y) 且 1 ≤ x ≤ b , 1 ≤ y ≤ d 1\leq x\leq b,1\leq y\leq d 1≤x≤b,1≤y≤d 满足条件的对数。
显然,
F
(
i
)
=
∑
i
∣
j
f
(
j
)
F(i)=\sum_{i|j}f(j)
F(i)=∑i∣jf(j),且
F
(
i
)
=
b
i
×
d
i
F(i)=\frac{b}{i}\times\frac{d}{i}
F(i)=ib×id,所以:
f
(
i
)
=
∑
i
∣
j
μ
(
j
i
)
×
F
(
j
)
f(i)=\sum_{i|j}\mu(\frac{j}{i})\times F(j)
f(i)=i∣j∑μ(ij)×F(j)我们要求的就是
f
(
1
)
f(1)
f(1),上面的式子支持数论分块,我们处理出
μ
\mu
μ的前缀和,就可以分块了,时间复杂度
O
(
n
)
O(\sqrt n)
O(n),先贴一个简化版的代码。
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
const int M = 100005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int T,Cases,ans,tmp,n,m,k,cnt,vis[M],p[M],mu[M];
void init(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
mu[i]=-1;
p[++cnt]=i;
}
for(int j=1;j<=cnt && i*p[j]<=n;j++)
{
vis[i*p[j]]=1;
if(i%p[j]==0) break;
mu[i*p[j]]=-mu[i];
}
}
for(int i=2;i<=n;i++)
mu[i]=mu[i-1]+mu[i];
}
signed main()
{
T=read();
init(1e5);
while(T--)
{
read();n=read();read();m=read();k=read();
printf("Case %d: ",++Cases);
if(k==0)
{
puts("0");
continue;
}
n/=k;m/=k;
if(n>m) swap(n,m);
tmp=ans=0;
for(int l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
}
for(int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
tmp+=(mu[r]-mu[l-1])*(n/l)*(n/l);
}
printf("%lld\n",ans-tmp/2);
}
}
然后我们使用容斥,答案为: f ( b k , d k ) − f ( b k , c − 1 k ) − f ( a − 1 k , d k ) + f ( a − 1 k , c − 1 k ) f(\frac{b}{k},\frac{d}{k})-f(\frac{b}{k},\frac{c-1}{k})-f(\frac{a-1}{k},\frac{d}{k})+f(\frac{a-1}{k},\frac{c-1}{k}) f(kb,kd)−f(kb,kc−1)−f(ka−1,kd)+f(ka−1,kc−1),还是一样的复杂度,相同的操作跑四遍就可以了,贴个代码 q w q qwq qwq。
#include <cstdio>
#include <iostream>
using namespace std;
#define ll long long
const int M = 50005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int T,Cases,a,b,c,d,k;
int cnt,vis[M],p[M],mu[M];
void init(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
mu[i]=-1;
p[++cnt]=i;
}
for(int j=1;j<=cnt && i*p[j]<=n;j++)
{
vis[i*p[j]]=1;
if(i%p[j]==0) break;
mu[i*p[j]]=-mu[i];
}
}
for(int i=2;i<=n;i++)
mu[i]=mu[i-1]+mu[i];
}
ll get(int n,int m)
{
if(n>m) swap(n,m);
ll ans=0;
for(int l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans+=1ll*(mu[r]-mu[l-1])*(n/l)*(m/l);
}
return ans;
}
signed main()
{
T=read();
init(5e4);
while(T--)
{
a=read()-1;b=read();c=read()-1;d=read();k=read();
if(k==0)
{
puts("0");
continue;
}
printf("%lld\n",get(b/k,d/k)-get(a/k,d/k)-get(b/k,c/k)+get(a/k,c/k));
}
}