bzoj2301 莫比乌斯反演

来源:互联网 发布:淘宝有名片设计吗 编辑:程序博客网 时间:2024/05/20 04:46

传送门

题意:给出a、b、c、d、k,求在[a,b]内取一个数x,在[c,d]内取一个数y,使得gcd(x,y)=k的方法数

思路:可以说是hdu1695的升级版了,相当于在[a/k,b/k]中取一个x,在[c/k,d/k]中取取一个y,使得gcd(x,y)=1的方法数,定义一个solve函数,作用是求[1,m]内取x,[1,n]内取y,使得gcd(x,y)=1的方法数,根据hdu1695的思路,则f(1)=F(1)*mu(1)+...+F(min(x,y))*mu(min(x,y)),但如果还是这样做会超时,所以要用分块优化,贴一下分块优化的代码,其中的sum是莫比乌斯函数的前缀和:

int solve(int m,int n){    int last;    int ans=0;    for(int i=1;i<=min(m,n);i=last+1)    {        last=min(n/(n/i),m/(m/i));        ans+=(n/i)*(m/i)*(sum[last]-sum[i-1]);    }    return ans;}

然后再用一下容斥,最后的结果为

ans=solve(b/k,d/k)-solve((a-1)/k,d/k)-solve((c-1)/k,b/k)+solve((a-1)k,(c-1)/k)

因为我们需要求得[a,b]和[c,d]这两个区间,仔细一想就明白了

还有一点这题必须要用int,用long long会WA,非常不解(??????)

完整代码:

#include<iostream>#include<cstring>#include<cstdio>using namespace std;typedef long long LL ;const int N=50005;bool check[N+5];int prime[N+5];int mu[N+5];int sum[N+5];int tot;void miu(){    memset(check,false,sizeof(check));    mu[1]=1;    tot=0;    for(int i=2;i<=N;i++)    {        if(!check[i])        {            prime[tot++]=i;            mu[i]=-1;        }        for(int j=0;j<tot;j++)        {            if(i*prime[j]>N) break;            check[i*prime[j]]=true;            if(i%prime[j]==0)            {                mu[i*prime[j]]=0;                break;            }            else                mu[i*prime[j]]=-mu[i];        }    }    sum[0]=0;    for(int i=1;i<=N;i++)        sum[i]=sum[i-1]+mu[i];}int solve(int m,int n){    int last;    int ans=0;    for(int i=1;i<=min(m,n);i=last+1)    {        last=min(n/(n/i),m/(m/i));        ans+=(n/i)*(m/i)*(sum[last]-sum[i-1]);    }    return ans;}int main(){    miu();    int t;    scanf("%d",&t);    while(t--)    {        int a,b,c,d,k;        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);        int ans=solve(b/k,d/k)-solve((a-1)/k,d/k)-        solve((c-1)/k,b/k)+solve((a-1)/k,(c-1)/k);        printf("%d\n",ans);    }    return 0;}