Description
Input
第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k
Output
共n行,每行一个整数表示满足要求的数对(x,y)的个数
Sample Input
Sample Output
HINT
100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
题解:
,(题解中的容斥定理把c和b写反了,无视就好)
总结一下,莫比乌斯反演有两种形式
形式一:
形式二:
本题用到的是第二种形式,题目要求求x∈[a,b],y∈[c,d]。x和y满足gcd(x,y)==k的x和y的数对数。
令f(i)为x∈[1,N],y∈[1,M],gcd(x,y)==i 的(x,y)数对数。
令F(i)为x∈[1,N],y∈[1,M],i|gcd(x,y) 的(x,y)数对数。
可以感觉,a和c的限制有些不好处理,不妨先忘掉a,c的限制。再看形式二,考虑f(i)与F(i)的关系,f(i)是gcd(x,y)==i,F(i)是gcd(x,y)==t*i(t∈Z),所以F(i)=sigma(f(d))(i|d),所以f(i)与F(i)正好满足形式二的第一个,可以利用莫比乌斯反演用F(i)表示f(i)。而F[i]=[N/i]*[M/i] ([]表示向下取整)
再考虑a和c的限制,设g(x,y,k)表示[1,x],[1,y] gcd(x1,y1)==k的对数,这个式子其实等价于g(x/k,y/k,1)。
再有容斥原理可得:Ans=g(b/k,d/k,1)-g((a-1)/k,d/k,1)-g(c/k,b/k,1)+g((a-1)/k,(c-1)/k,1),对于每个g(,,)我们都可以用f()来求。
O(n)的复杂度不足以解决此题,还有一个O(sqrt(n))的优化,具体看ppt或题解,讲的很清楚。
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 using namespace std;10 typedef long long LL; 11 const LL maxn=50000; 12 LL mu[maxn],prime[maxn],sum[maxn],tot,N;13 bool not_prime[maxn];14 LL a,b,c,d,k,ANS;15 inline void pre(){16 memset(not_prime,false,sizeof(not_prime));17 mu[1]=1;18 for(LL i=2;i<=maxn;i++){19 if(not_prime[i]==false){20 prime[++tot]=i;21 mu[i]=-1;22 }23 for(LL j=1;j<=tot;j++){24 if(i*prime[j]>maxn) break;25 not_prime[prime[j]*i]=true;26 if(i%prime[j]==0){27 mu[prime[j]*i]=0;28 break;29 }30 else mu[prime[j]*i]=-mu[i];31 }32 }33 }34 inline LL solve(LL N,LL M){35 LL ans=0;36 if(N>M) swap(N,M);37 for(LL i=1,la=0;i<=N;i=la+1){38 la=min(N/(N/i),M/(M/i));39 ans+=(sum[la]-sum[i-1])*(N/i)*(M/i);40 }41 return ans;42 } 43 int main(){44 // freopen("b.in","r",stdin);45 // freopen("b.out","w",stdout);46 pre();47 sum[0]=0;48 for(LL i=1;i<=maxn;i++){49 sum[i]=sum[i-1]+mu[i];50 }51 scanf("%d",&N);52 while(N--){53 scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);54 ANS=solve(b/k,d/k)-solve((a-1)/k,d/k)-solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k);55 printf("%lld\n",ANS);56 }57 return 0;58 }