2301: [HAOI2011]Problem b
Time Limit: 50 Sec Memory Limit: 256 MB
Submit: 7732 Solved: 3750
[Submit][Status][Discuss]
Description
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
Input
第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k
Output
共n行,每行一个整数表示满足要求的数对(x,y)的个数
Sample Input
2
2 5 1 5 1
1 5 1 5 2
Sample Output
14
3
题解:简化为计算a/k<=x<=b/k,c/k<=y<=d/k满足gcd(x,y)=1的x,y有多少对;cal(n,m)代表1<=x<=n,1<=y<=m满足gcd(x,y)=1的(x,y)对数,则根据容斥定理:
#include<iostream>
#include<stdio.h>
#define ll long long
using namespace std;
const ll N=50007;
ll prime[N],mu[N];
bool mark[N];
void getmu()
{
mu[1]=1;
int cnt=0;
for(int i=2;i<=N;i++){
if(!mark[i])
prime[cnt++]=i,mu[i]=-1;
for(int j=0;j<cnt&&i*prime[j]<=N;j++){
mark[i*prime[j]]=1;
if(i%prime[j]){
mu[i*prime[j]]=-mu[i];
}else{
mu[i*prime[j]]=0;break;
}
}
mu[i]+=mu[i-1];//后面不会再用到mu[i],所以可以直接记为前缀和
}
}
ll cal(ll n,ll m){
if(n>m)
swap(n,m);
ll ans=0;
for(ll l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
}
return ans;
}
int main()
{
int T;
ll a,b,c,d,k;
getmu();
scanf("%d",&T);
while(T--){
scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
a=(a-1)/k,b=b/k,c=(c-1)/k,d=d/k;
printf("%lld\n",cal(b,d)-cal(a,d)-(cal(c,b)-cal(c,a)));
}
return 0;
}