题目链接:点击这里
题目大意:对于给出的 n n n 个询问,每次求有多少个数对 ( x , y ) (x,y) (x,y) ,满足 a ≤ x ≤ b a \le x \le b a≤x≤b , c ≤ y ≤ d c \le y \le d c≤y≤d,且 gcd ( x , y ) = k \gcd(x,y) = k gcd(x,y)=k
题目分析:
此题目所求式子为:
∑
i
=
a
b
∑
j
=
c
d
[
gcd
(
i
,
j
)
=
k
]
\sum_{i=a}^b\sum_{j=c}^d[\gcd(i,j)=k]
i=a∑bj=c∑d[gcd(i,j)=k]
我们继续套路的设:
f
(
d
)
=
∑
i
=
1
N
∑
j
=
1
M
[
gcd
(
i
,
j
)
=
d
]
f(d)=\sum_{i=1}^N\sum_{j=1}^M[\gcd(i,j)=d]
f(d)=i=1∑Nj=1∑M[gcd(i,j)=d]
F
(
n
)
=
∑
n
∣
d
f
(
d
)
F(n)=\sum_{n|d}f(d)
F(n)=n∣d∑f(d)
=
⌊
N
n
⌋
⌊
M
n
⌋
=\lfloor \frac Nn \rfloor \lfloor \frac Mn \rfloor
=⌊nN⌋⌊nM⌋
由莫比乌斯反演可得:
f
(
n
)
=
∑
n
∣
d
μ
(
d
n
)
F
(
d
)
f(n)=\sum_{n|d}μ(\frac dn)F(d)
f(n)=n∣d∑μ(nd)F(d)
重新选取枚举项,令
x
=
d
n
x=\frac dn
x=nd ,有:
f
(
n
)
=
∑
x
=
1
m
i
n
(
N
,
M
)
μ
(
x
)
F
(
n
x
)
f(n)=\sum_{x=1}^{min(N,M)}μ(x)F(nx)
f(n)=x=1∑min(N,M)μ(x)F(nx)
=
∑
x
=
1
m
i
n
(
N
,
M
)
μ
(
x
)
⌊
N
n
x
⌋
⌊
M
n
x
⌋
=\sum_{x=1}^{min(N,M)}μ(x)\lfloor \frac N {nx} \rfloor \lfloor \frac M {nx} \rfloor
=x=1∑min(N,M)μ(x)⌊nxN⌋⌊nxM⌋
此时我们对
μ
(
x
)
μ(x)
μ(x) 求个前缀和就可以在
O
(
n
)
O(\sqrt n)
O(n) 内求出
∑
i
=
1
N
∑
j
=
1
M
[
gcd
(
i
,
j
)
=
k
]
\sum_{i=1}^N\sum_{j=1}^M[\gcd(i,j)=k]
∑i=1N∑j=1M[gcd(i,j)=k] 了
题目所求式子可以通过简单容斥求解:
设
g
(
n
,
m
)
=
∑
i
=
1
N
∑
j
=
1
M
[
gcd
(
i
,
j
)
=
k
]
g(n,m)=\sum_{i=1}^N\sum_{j=1}^M[\gcd(i,j)=k]
g(n,m)=∑i=1N∑j=1M[gcd(i,j)=k]
则
a
n
s
=
g
(
b
,
d
)
−
g
(
a
−
1
,
d
)
−
g
(
b
,
c
−
1
)
+
g
(
a
−
1
,
c
−
1
)
ans=g(b,d)-g(a-1,d)-g(b,c-1)+g(a-1,c-1)
ans=g(b,d)−g(a−1,d)−g(b,c−1)+g(a−1,c−1)
时间复杂度为
O
(
t
n
)
O(t\sqrt n)
O(tn)
具体细节见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e5+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int cnt,pri[maxn],mu[maxn],sum[maxn];
bool vis[maxn];
void get_mu()
{
mu[1] = vis[1] = 1;
for(int i = 2;i < maxn;i++)
{
if(!vis[i])
{
mu[i] = -1;
pri[++cnt] = i;
}
for(int j = 1;j <= cnt && i*pri[j] < maxn;j++)
{
vis[i*pri[j]] = true;
if(i%pri[j] == 0) break;
else mu[i*pri[j]] -= mu[i];
}
}
for(int i = 1;i < maxn;i++)
sum[i] = sum[i-1]+mu[i];
}
ll calc(int n,int m,int d)
{
ll res = 0;
for(int l = 1,r;l <= min(n,m);l = r+1)
{
r = min(n/(n/l),m/(m/l));
res += 1ll*(sum[r]-sum[l-1])*(n/(d*l))*(m/(d*l));
}
return res;
}
int main()
{
get_mu();
int n = read();
while(n--)
{
int a = read(),b = read(),c = read(),d = read(),k = read();
ll ans = calc(b,d,k)-calc(a-1,d,k)-calc(b,c-1,k)+calc(a-1,c-1,k);
printf("%lld\n",ans);
}
return 0;
}