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

xk i yk j ,把区间[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值