[BZOJ 2301][HAOI2011]Problem b(莫比乌斯反演)

Description

对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

Solution

还是要学习一个

莫比乌斯反演…

进行优化,对mu维护一个前缀和

学习资料的话推荐PoPoQQQ的ppt,还有莫比乌斯反演(宋新波)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MAXN 50005
#define Min(a,b) (a<b?a:b)
using namespace std;
typedef long long LL;
int n,a,b,c,d,k;
int mu[MAXN],sum[MAXN],pri[MAXN],cnt=0;
bool jud[MAXN];
void getmu()
{
    mu[1]=1;
    for(int i=2;i<=MAXN;i++)
    {
        if(!jud[i])pri[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&pri[j]*i<=MAXN;j++)
        {
            jud[pri[j]*i]=1;
            if(i%pri[j]==0){mu[pri[j]*i]=0;break;}
            mu[pri[j]*i]=-mu[i];
        }
    }
    for(int i=1;i<=MAXN;i++)
    sum[i]=sum[i-1]+mu[i];
}
LL clac(int m,int n,int k)
{
    LL res=0;int last;
    m/=k,n/=k;
    for(int i=1;i<=Min(n,m);i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        res+=(LL)(n/i)*(m/i)*(sum[last]-sum[i-1]);
    }
    return res;
}
int main()
{
    scanf("%d",&n);getmu();
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        printf("%lld\n",clac(b,d,k)-clac(a-1,d,k)-clac(b,c-1,k)+clac(a-1,c-1,k));
    }
    return 0;
}

 

posted @ 2017-04-09 16:03  Zars19  阅读(230)  评论(0编辑  收藏  举报