有多少对 x属于[a,b] , y属于[c,d], 满足 gcd(x,y)
思路:
直接对于[a,b],[c,d]中的每个gcd(x,y)=k的倍数进行容斥,
1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000 不记莫比乌斯打表的时间,
也会T
看了不少博客,这个题目用了些技巧。
分块:
以前求用过这里两个数就不那么容易想到了,我们知道由于整形除法的一些性质,
[n/2+1,n],[n/3+1,n/2],[n/4+1,n/3],n/i都是定值
同样,两个数的时候也用一定的区间,[l,r] 会有 a/i ,b/i同时是定值
这样我们求出gcd(x,y)=i 的个数都将等于 (a/i)*(b/i),
系数莫比乌斯函数项,我们可以用去区间和的
在[l,r] ans=sigma(mu[i]*(a/i)*(b/i))=[sigma(mu[i])]*(a/i)*(b/i);
求出[sigma(mu[i])]只要前缀和相减就行
那么两个数怎么分块呢?
注意到
比如 l=100, r=200
那么 i=34 。。。。。 40 (l / i) * (r / i)都是一样的 2*5
那么 i=41 。。。。。 50 (l / i) * (r / i)都是一样的 2*4
那么 i=51 。。。。。 66 (l / i) * (r / i)都是一样的 1*3
34-40,41-50 51-66 是怎么来的呢?
34 = 33+1;
40 = min(l/(l/34) , r/(r/34));
41 = 40+1;
50 = min(l/(l/41) , r/(r/41));
51 = 50+1;
66 = min(l/(l/51) , r/(r/51));
我们看到(l/i=2)区间还没断,但是(r/i=5)的区间已经结束了
两个数把各自的分块区间打断了,但是好在时间复杂度并没用太大的改变
容斥:
分块可以降低很大的时间复杂度,但是明显不太好处理区间,我们通常都是求出[1,n]
的结果,所以我们可以用到容斥
ans([a,b],[c , d])
=ans([1,b],[1,d])-ans([1,b],[1,c-1])-ans([1,a-1],[1,c])+ans([1,a-1],[1,c-1]);
#include<bits/stdc++.h>
using namespace std;
#define MAXN 50000
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
int sum[MAXN+10];
void Moblus()
{
memset(check,false,sizeof(check));
mu[1]=1;
int tot = 0;
for(int i = 2; i <= MAXN; i++)
{
if( !check[i] )
{
prime[tot++] = i;
mu[i] = -1;
}
for(int j = 0; j < tot; j++)
{
if(i * prime[j] > MAXN) break;
check[i * prime[j]] = true;
if( i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
sum[0]=0;
for (int i = 1; i <= MAXN; i++)
{
sum[i] = sum[i - 1] + mu[i];
}
}
// 利用莫比乌斯反演 找[1, n], [1, m]内gcd(i,j)=1的数对的个数
/*
O(n) 枚举(1,l)会T
ans += mob[i] * (l / i) * (r / i)
注意到
比如 l=100, r=200
那么 i=34 。。。。。 40 (l / i) * (r / i)都是一样的 2*5
那么 i=41 。。。。。 50 (l / i) * (r / i)都是一样的 2*4
那么 i=51 。。。。。 66 (l / i) * (r / i)都是一样的 1*3
当 i较大的时候,有些答案可以是mu[i]的前缀和乘以一个定值
34-40,41-50 51-66 是怎么来的呢?
34 = 33+1;
40 = min(l/(l/34) , r/(r/34));
41 = 40+1;
50 = min(l/(l/41) , r/(r/41));
51 = 50+1;
66 = min(l/(l/51) , r/(r/51));
说白了,也就是找可以一起取定值的区间
其实也就类似在[n/2+1,n],[n/3+1,n/2]取的值是定值而可以减少计算
这里有两个变量 ,两个变量的区间不同步,但是在一端小区间依然是定值
*/
long long s(int n,int m)
{
long long ans = 0;
if (n > m) swap(n, m);
for (int i = 1, la = 0; i <= n; i = la + 1)
{
la = min(n / (n / i), m / (m / i));
ans += (long long)(sum[la] - sum[i - 1]) * (n / i) * (m / i);
}
return ans;
}
int main()
{
Moblus() ;
int n;
int a,b,c,d,k;
scanf("%d",&n);
while(n--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
long long ans=s(b/k,d/k)-s((a-1)/k,d/k)-s((c-1)/k,b/k)+s((a-1)/k,(c-1)/k);
printf("%lld\n",ans);
}
}