bzoj 2301: [HAOI2011]Problem b
Description
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
Input
第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k
Output
共n行,每行一个整数表示满足要求的数对(x,y)的个数
解题报告:
这题其实和上题差不多,只需要容斥一波即可,如何反演参见上题,此题中不能暴力统计答案,需要一个优化.
观察到\(G_n=(a/i)*(b/i)\)这个式子,很多时候i变化,但(a/i)和(b/i)不会变化,其实(a/i)只有\(\sqrt{a}\)种取值,所以我们我们可以直接跳着统计,此时记一个\(\mu\)的前缀和即可
复杂度:\(O(n\sqrt{n})\)
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=50005;
int num=0,prime[N],phi[N],sum[N];bool vis[N];
void prework(){
int to;phi[1]=1;
for(int i=2;i<N;i++){
if(!vis[i]){
prime[++num]=i;
phi[i]=-1;
}
for(int j=1;j<=num && prime[j]*i<N;j++){
to=prime[j]*i;vis[to]=true;
if(i%prime[j])phi[to]=-phi[i];
else{
phi[to]=0;
break;
}
}
}
for(int i=1;i<N;i++)sum[i]=sum[i-1]+phi[i];
}
ll solve(int a,int b,int k){
a/=k;b/=k;if(a>b)swap(a,b);
ll ret=0,nxt;
for(int i=1;i<=a;i=nxt+1){
nxt=Min(a/(a/i),b/(b/i));
ret+=(ll)(a/i)*(b/i)*(sum[nxt]-sum[i-1]);
}
return ret;
}
void work()
{
int a,b,c,d,k;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
ll ans=solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k);
printf("%lld\n",ans);
}
int main()
{
int T;cin>>T;
prework();
while(T--)work();
return 0;
}