BZoj 2301 Problem b(容斥定理+莫比乌斯反演)

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)对数,则根据容斥定理:

\small {\color{Red} ans=\frac{cal(b,d)-cal(a-1,d)}{a<=x<=b,1<=y<=b}-\frac{cal(b,c-1)-cal(a-1,c-1))}{a<=x<=b,1<=y<=c}}

#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;
}

 

posted @ 2018-12-27 20:56  aeipyuan  阅读(112)  评论(0编辑  收藏  举报