BZOJ2301: [HAOI2011]Problem b

【传送门:BZOJ2301


简要题意:

  给出a,b,c,d,k,求出满足a<=x<=b,c<=y<=d的数对中gcd(x,y)==k的数对数


题解:

  莫比乌斯反演模板题

  设F(t)为gcd(x,y)%t==0的数对数,f(t)为gcd(x,y)==t的数对数

  然后可以得到$F(n)=\sum_{n|d}f(d)$,根据公式得到$f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)$

  因为F(t)=b/t*d/t,所以可以得到$f(1)=\sum_{i=1}^{min(b/k,d/k)}\mu(i)*\frac{b}{k}*\frac{d}{k}$

  然后整除分块加速,容斥一下就可以了


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
int miu[51000],prime[51000],v[51000];
int sum[51000];
void pre(int n)
{
    memset(v,0,sizeof(v));
    memset(miu,0,sizeof(miu));miu[1]=1;
    int m=0;
    for(int i=2;i<=n;i++)
    {
        if(v[i]==0)
        {
            v[i]=i;
            prime[++m]=i;
            miu[i]=-1;
        }
        for(int j=1;j<=m;j++)
        {
            if(prime[j]>v[i]||prime[j]>n/i) break;
            v[i*prime[j]]=prime[j];
            if(i%prime[j]==0){miu[i*prime[j]]=0;break;}
            else miu[i*prime[j]]=-miu[i];
        }
    }
    sum[0]=0;
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+miu[i];
}
LL getd(int x,int y)
{
    int last;LL ans=0;
    for(int i=1;i<=min(x,y);i=last+1)
    {
        last=min(x/(x/i),y/(y/i));
        ans+=(LL)(sum[last]-sum[i-1])*(x/i)*(y/i);
    }
    return ans;
}
int main()
{
    pre(50000);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        LL ans1=0,ans2=0;
        printf("%lld\n",getd(b/k,d/k)-getd((a-1)/k,d/k)-getd(b/k,(c-1)/k)+getd((a-1)/k,(c-1)/k));
    }
    return 0;
}

 

posted @ 2018-10-24 08:34  Star_Feel  阅读(...)  评论(...编辑  收藏