对于给出的n个询问,每次求有多少个数对 ( x , y ) (x,y) (x,y),满足 a ≤ x ≤ b a≤x≤b a≤x≤b, c ≤ y ≤ d c≤y≤d c≤y≤d,且 g c d ( x , y ) = k gcd(x,y) = k gcd(x,y)=k, g c d ( x , y ) gcd(x,y) gcd(x,y)函数为 x x x和 y y y的最大公约数.
即求式子 ∑ x = a b ∑ y = c d [ g c d ( x , y ) = k ] \sum_{x=a}^b\sum_{y=c}^d[gcd(x,y)=k] ∑x=ab∑y=cd[gcd(x,y)=k].
记 f ( n , m ) = ∑ x = 1 n ∑ y = 1 m [ g c d ( x , y ) = k ] f(n,m)=\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)=k] f(n,m)=∑x=1n∑y=1m[gcd(x,y)=k]
根据二维前缀和公式,可以将式子转换成:
∑ x = a b ∑ y = c d [ g c d ( x , y ) = k ] = f ( b , d ) + f ( a − 1 , c − 1 ) − f ( a − 1 , d ) − f ( b , c − 1 ) \sum_{x=a}^b\sum_{y=c}^d[gcd(x,y)=k]=f(b,d)+f(a-1,c-1)-f(a-1,d)-f(b,c-1) ∑x=ab∑y=cd[gcd(x,y)=k]=f(b,d)+f(a−1,c−1)−f(a−1,d)−f(b,c−1)
因此我们只要能得到 f ( n , m ) f(n,m) f(n,m)的计算方法即可.
求 f ( n , m ) f(n,m) f(n,m)的套路非常明显:莫比比乌斯反演
由于 ∑ k ∣ d ∑ x = 1 n ∑ y = 1 m [ g c d ( x , y ) = d ] = ⌊ n k ⌋ ⌊ m k ⌋ \sum_{k|d}\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)=d] = \lfloor \frac{n}{k} \rfloor \lfloor \frac{m}{k} \rfloor ∑k∣d∑x=1n∑y=1m[gcd(x,y)=d]=⌊kn⌋⌊km⌋.
反演得到 f ( n , m ) = ∑ k ∣ d μ ( d k ) ⌊ n d ⌋ ⌊ m d ⌋ f(n,m)=\sum_{k|d}\mu(\frac{d}{k})\lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor f(n,m)=∑k∣dμ(kd)⌊dn⌋⌊dm⌋
令 t = d / k t = d/k t=d/k.
f ( n , m ) = ∑ t = 1 n / d μ ( t ) ⌊ n k t ⌋ ⌊ m k t ⌋ f(n,m)=\sum_{t=1}^{n/d}\mu(t)\lfloor \frac{n}{kt} \rfloor \lfloor \frac{m}{kt} \rfloor f(n,m)=∑t=1n/dμ(t)⌊ktn⌋⌊ktm⌋
对上式子进行分块计算,可以将时间复杂度从 O ( n ) O(n) O(n)降至 O ( n ) O(\sqrt{n}) O(n).
对于形如 f ( x ) = ∑ x ∣ d μ ( d x ) g ( ⌊ n d ⌋ ) f(x)=\sum_{x|d}\mu(\frac{d}{x})g(\lfloor \frac{n}{d} \rfloor) f(x)=∑x∣dμ(xd)g(⌊dn⌋)这样的式子,我们都可以用 t = d / x t=d/x t=d/x代换后数论分块进行加速.
f ( x ) = ∑ t = 1 n / x μ ( t ) g ( ⌊ n x t ⌋ ) f(x)=\sum_{t=1}^{n/x}\mu(t)g(\lfloor \frac{n}{xt} \rfloor) f(x)=∑t=1n/xμ(t)g(⌊xtn⌋)
时间复杂度从 O ( n ) O(n) O(n)降至 O ( n ) O(\sqrt{n}) O(n).
#include
#include
#include
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
const int N = 50000;
int prime[N+10],zhi[N+10],mu[N+10],pcnt;
void sieve() {
zhi[1] = mu[1] = 1;
for(int i = 2;i <= N;++i) {
if(!zhi[i]) {
mu[i] = -1;
prime[pcnt++] = i;
}
for(int j = 0;j < pcnt && prime[j]*i <= N;++j) {
zhi[i*prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i*prime[j]] = 0;
break;
}
else
mu[i*prime[j]] = -mu[i];
}
}
for(int i = 1;i <= N;++i) mu[i] += mu[i-1];
}
int a,b,c,d,k,T;
int calc(int n,int m) {
int ans = 0;
int lim = std::min(n/k,m/k);
for(int i = 1,nx1,nx2,nxt;i <= lim;i=nxt+1) {
nx1 = n/(n/i);
nx2 = m/(m/i);
nxt = nx1>nx2?nx2:nx1;
ans += (mu[nxt]-mu[i-1])*(n/i/k)*(m/i/k);
}
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
sieve();
std::cin >> T;
while(T--) {
std::cin >> a >> b >> c >> d >> k;
int ans = calc(b,d)+calc(a-1,c-1)-calc(a-1,d)-calc(b,c-1);
std::cout << ans << std::endl;
}
return 0;
}