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;
}
posted @ 2017-09-02 01:18  PIPIBoss  阅读(...)  评论(...编辑  收藏