Bzoj P2301 [HAOI2011]Problem b___莫比乌斯反演+容斥

本文介绍了一种高效算法,用于解决特定数学问题:在给定范围内,计算满足特定最大公约数条件的数对数量。通过巧妙运用莫比乌斯函数和容斥原理,实现对数对的有效枚举。

题目大意:

对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

1 ≤ n ≤ 50000 , 1 ≤ a ≤ b ≤ 50000 1≤n≤50000,1≤a≤b≤50000 1n500001ab50000
1 ≤ c ≤ d ≤ 50000 , 1 ≤ k ≤ 50000 1≤c≤d≤50000,1≤k≤50000 1cd500001k50000

分析:

d x , y d_{x,y} dx,y表示 ∑ i = 1 x ∑ j = 1 y [ g c d ( i , j ) = k ] \sum_{i=1}^{x}\sum_{j=1}^{y}[gcd(i,j)=k] i=1xj=1y[gcd(i,j)=k]
那答案容斥一下可以知道就是 d b , d − d a − 1 , d − d b , c − 1 + d a − 1 , c − 1 d_{b,d}-d_{a-1,d}-d_{b,c-1}+d_{a-1,c-1} db,dda1,ddb,c1+da1,c1
那么我们考虑求 d x , y d_{x,y} dx,y
d x , y d_{x,y} dx,y
= ∑ i = 1 x ∑ j = 1 y g c d ( i , j ) = k \sum_{i=1}^{x}\sum_{j=1}^{y}gcd(i,j)=k i=1xj=1ygcd(i,j)=k
= ∑ i = 1 ⌊ x k ⌋ ∑ j = 1 ⌊ y k ⌋ [ g c d ( i , j ) = 1 ] =\sum_{i=1}^{\left \lfloor \frac{x}{k} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{y}{k} \right \rfloor}[gcd(i,j)=1] =i=1kxj=1ky[gcd(i,j)=1]
我们知道莫比乌斯函数有一个性质,
∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}μ(d)=[n=1] dnμ(d)=[n=1]
那么当 g c d ( i , j ) = 1 gcd(i,j)=1 gcd(i,j)=1时,
显然 ∑ d ∣ g c d ( i , j ) μ ( d ) = 1 \sum_{d|gcd(i,j)}μ(d)=1 dgcd(i,j)μ(d)=1
那么
∑ i = 1 ⌊ x k ⌋ ∑ j = 1 ⌊ y k ⌋ [ g c d ( i , j ) = 1 ] \sum_{i=1}^{\left \lfloor \frac{x}{k} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{y}{k} \right \rfloor}[gcd(i,j)=1] i=1kxj=1ky[gcd(i,j)=1]
就可以写成
= ∑ i = 1 ⌊ x k ⌋ ∑ j = 1 ⌊ y k ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) =\sum_{i=1}^{\left \lfloor \frac{x}{k} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{y}{k} \right \rfloor} \sum_{d|gcd(i,j)}μ(d) =i=1kxj=1kydgcd(i,j)μ(d)
然后这个东西可以转换一下,安利一个博客,
https://www.cnblogs.com/NaVi-Awson/p/8318709.html
在这里插入图片描述
然后我们对于所有的 ⌊ n k d ⌋ \left \lfloor \frac{n}{kd} \right \rfloor kdn,可以发现取值最多只有 n \sqrt{n} n 种,并且相同的都是连续的一段,那么我们就可以愉快的预处理出 m o b i u s mobius mobius函数的前缀和,然后分块处理 ⌊ n k d ⌋ \left \lfloor \frac{n}{kd} \right \rfloor kdn,就可以求出 = ∑ i = 1 ⌊ x k ⌋ ∑ j = 1 ⌊ y k ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) =\sum_{i=1}^{\left \lfloor \frac{x}{k} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{y}{k} \right \rfloor} \sum_{d|gcd(i,j)}μ(d) =i=1kxj=1kydgcd(i,j)μ(d),就可以让每次查询的复杂度都达到根号级别。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#define N 50005
 
using namespace std;
 
typedef long long ll;
 
int prime[N], check[N], sum[N], mu[N], T, a, b, c, d, k, cnt;
 
void Get_mobius()
{
    mu[1] = 1;
    for (int i = 2; i <= 50000; i++)
    {
         if (!check[i])
         {
             prime[++cnt] = i;
             mu[i] = -1;
         }
         for (int j = 1; j <= cnt; j++)
         {
              if (prime[j] * i > 50000) break;
              check[prime[j] * i] = 1;
              if (i % prime[j] == 0) { mu[prime[j] * i] = 0; break; }
                                else mu[prime[j] * i] = - mu[i];
         }
    }
    for (int i = 1; i <= 50000; i++) sum[i] = sum[i - 1] + mu[i];
}
 
ll Cal(int x, int y) 
{
    ll num = 0;
    if (x > y) swap(x, y);
    for (int i = 1, last; i <= x; i = last + 1)
    {
         last = min(x / (x / i), y / (y / i));
         num += (ll)(sum[last] - sum[i - 1]) * (x  / i) * (y / i);
    }
    return num;
}
 
int main()
{
    Get_mobius();
    scanf("%d", &T);
    while (T--)
    {
           scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
           ll ans = Cal(b / k, d / k) - Cal((a - 1) / k, d / k) - Cal(b / k, (c - 1) / k) + Cal((a - 1) / k, (c - 1) / k);
           printf("%lld\n", ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值