题目描述
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
输入输出格式
输入格式:
第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k
输出格式:
共n行,每行一个整数表示满足要求的数对(x,y)的个数
输入输出样例
说明
100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
题意:求出满足该式子的区间里的对数
思路:莫比乌斯反演
和前面破译密码那道非常类似,这里是限制了区间是在 [a,b] 与 [c,d] ,这里我们之前的做法只能求出 1-a 与 1-b的值
这么我们就需要容斥一下
g[a,b]代表1-a与 1-b的求出的值
所以我们可以得出 = g[b,d] - g[a-1,c] - g[b,c-1] + g[a-1,c-1]
然后再求值即可
#include<bits/stdc++.h> #define maxn 100005 #define mod 1000000007 using namespace std; typedef int ll; ll vis[maxn+10]; ll mu[maxn+10]; ll sum[maxn+10]; ll a,b,c,d; void init(){ for(int i=0;i<maxn;i++){ vis[i]=0; mu[i]=1; } for(int i=2;i<maxn;i++){ if(vis[i]==0){ mu[i]=-1; for(int j=2*i;j<maxn;j+=i){ vis[j]=1; if((j/i)%i==0) mu[j]=0; else mu[j]*=-1; } } } sum[1]=1; for(int i=2;i<maxn;i++){ sum[i]=sum[i-1]+mu[i]; } } ll g(ll x,ll y){ ll ans=0; if(x>y) swap(x,y); for(ll l=1,r=0;l<=x;l=r+1){ r=min(x/(x/l),y/(y/l)); ans+=(sum[r]-sum[l-1])*(x/l)*(y/l); } return ans; } int main(){ init(); ll t; ll k; scanf("%d",&t); while(t--){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); ll ans=g(b/k,d/k)-g((a-1)/k,d/k)-g(b/k,(c-1)/k)+g((a-1)/k,(c-1)/k); printf("%d\n",ans); } }