[HAOI2011]Problem b

一、题目

点此看题

二、解法

首先来看一个简化的问题, a = 1 a=1 a=1 c = 1 c=1 c=1的情况,我们先把范围都除以 k k k,问题就变成求互质数对的个数,考虑莫比乌斯反演。

f ( i ) f(i) f(i) gcd ⁡ ( x , y ) = i \gcd(x,y)=i gcd(x,y)=i 1 ≤ x ≤ b , 1 ≤ y ≤ d 1\leq x\leq b,1\leq y\leq d 1xb,1yd 满足条件的对数。

F ( i ) F(i) F(i) i ∣ gcd ⁡ ( x , y ) i|\gcd(x,y) igcd(x,y) 1 ≤ x ≤ b , 1 ≤ y ≤ d 1\leq x\leq b,1\leq y\leq d 1xb,1yd 满足条件的对数。

显然, F ( i ) = ∑ i ∣ j f ( j ) F(i)=\sum_{i|j}f(j) F(i)=ijf(j),且 F ( i ) = b i × d i F(i)=\frac{b}{i}\times\frac{d}{i} F(i)=ib×id,所以:
f ( i ) = ∑ i ∣ j μ ( j i ) × F ( j ) f(i)=\sum_{i|j}\mu(\frac{j}{i})\times F(j) f(i)=ijμ(ij)×F(j)我们要求的就是 f ( 1 ) f(1) f(1),上面的式子支持数论分块,我们处理出 μ \mu μ的前缀和,就可以分块了,时间复杂度 O ( n ) O(\sqrt n) O(n ),先贴一个简化版的代码。

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
const int M = 100005;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int T,Cases,ans,tmp,n,m,k,cnt,vis[M],p[M],mu[M];
void init(int n)
{
    mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])
        {
            mu[i]=-1;
            p[++cnt]=i;
        }
        for(int j=1;j<=cnt && i*p[j]<=n;j++)
        {
            vis[i*p[j]]=1;
            if(i%p[j]==0) break;
            mu[i*p[j]]=-mu[i];
        }
    }
    for(int i=2;i<=n;i++)
        mu[i]=mu[i-1]+mu[i];
}
signed main()
{
    T=read();
    init(1e5);
    while(T--)
    {
        read();n=read();read();m=read();k=read();
        printf("Case %d: ",++Cases);
        if(k==0)
        {
            puts("0");
            continue;
        }
        n/=k;m/=k;
        if(n>m) swap(n,m);
        tmp=ans=0;
        for(int l=1,r;l<=n;l=r+1)
        {
            r=min(n/(n/l),m/(m/l));
            ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
        }
        for(int l=1,r;l<=n;l=r+1)
        {
            r=n/(n/l);
            tmp+=(mu[r]-mu[l-1])*(n/l)*(n/l);
        }
        printf("%lld\n",ans-tmp/2);
    }
}

然后我们使用容斥,答案为: f ( b k , d k ) − f ( b k , c − 1 k ) − f ( a − 1 k , d k ) + f ( a − 1 k , c − 1 k ) f(\frac{b}{k},\frac{d}{k})-f(\frac{b}{k},\frac{c-1}{k})-f(\frac{a-1}{k},\frac{d}{k})+f(\frac{a-1}{k},\frac{c-1}{k}) f(kb,kd)f(kb,kc1)f(ka1,kd)+f(ka1,kc1),还是一样的复杂度,相同的操作跑四遍就可以了,贴个代码 q w q qwq qwq

#include <cstdio>
#include <iostream>
using namespace std;
#define ll long long
const int M = 50005;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int T,Cases,a,b,c,d,k;
int cnt,vis[M],p[M],mu[M];
void init(int n)
{
    mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])
        {
            mu[i]=-1;
            p[++cnt]=i;
        }
        for(int j=1;j<=cnt && i*p[j]<=n;j++)
        {
            vis[i*p[j]]=1;
            if(i%p[j]==0) break;
            mu[i*p[j]]=-mu[i];
        }
    }
    for(int i=2;i<=n;i++)
        mu[i]=mu[i-1]+mu[i];
}
ll get(int n,int m)
{
    if(n>m) swap(n,m);
    ll ans=0;
    for(int l=1,r;l<=n;l=r+1)
    {
        r=min(n/(n/l),m/(m/l));
        ans+=1ll*(mu[r]-mu[l-1])*(n/l)*(m/l);
    }
    return ans;
}
signed main()
{
    T=read();
    init(5e4);
    while(T--)
    {
        a=read()-1;b=read();c=read()-1;d=read();k=read();
        if(k==0)
        {
            puts("0");
            continue;
        }
        printf("%lld\n",get(b/k,d/k)-get(a/k,d/k)-get(b/k,c/k)+get(a/k,c/k));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值