bzoj2301 [HAOI2011]Problem b

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

题解:
分块+莫比乌斯反演
原式即

axbcydgcd(x,y)=k1

同样的,化式子:
akxkbkckykdkgcd(xk,yk)=11

xkiykj,把区间[ak,bk][ck,dk]容斥一下,就只需计算[1,n]这样的范围了。
于是式子就变成了:
1in1jmgcd(i,j)=11

1in1jmϵ(gcd(i,j))

因为有1μ=ϵ,即d|nμ(d)=ϵ(n)
所以式子变成:
1in1jmd|(i,j)μ(d)

1in1jmd|i,d|jμ(d)

于是套路变形:
1dmin(n,m)μ(d)×nd×md

然后就可以做了。
还是套路..对id×jd相等的进行分块,每次直接计算这段区间的和。但是因为d是会变的,所以计算完mobius之后要做个前缀和。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 50100

bool ispri[maxn];
int k,cnt,pri[maxn],sum[maxn],mu[maxn];
int mymin(int x,int y){return (x<y)?x:y;}
void mobius(int lim)
{
    cnt=0;sum[0]=0;mu[1]=1;
    for (int i=2;i<=lim;i++)
    {
        if (!ispri[i])
        {
            pri[++cnt]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=cnt && pri[j]*i<=lim;j++)
        {
            ispri[pri[j]*i]=true;
            if (i%pri[j]==0)
            {
                mu[i*pri[j]]=0;
                break;
            }
            mu[i*pri[j]]=-mu[i];
        }
    }
    for (int i=1;i<=50000;i++) sum[i]=sum[i-1]+mu[i];
}
int get(int n,int m)
{
    int lim,ret,x,y,lx,ly,r;
    ret=0;n/=k;m/=k;
    lim=mymin(n,m);
    for (int i=1;i<=lim;i=r+1)
    {
        x=n/i;y=m/i;
        lx=mymin(lim,n/x);
        ly=mymin(lim,m/y);
        r=mymin(lx,ly);
        ret+=(sum[r]-sum[i-1])*x*y;
    }
    return ret;
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int T,a,b,c,d;
    scanf("%d",&T);
    mobius(50000);
    while (T--)
    {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        int ans=get(b,d)-get(a-1,d)-get(b,c-1)+get(a-1,c-1);
        printf("%d\n",ans);
    }
    return 0;
}
发布了202 篇原创文章 · 获赞 21 · 访问量 11万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览