[HAOI2011]Problem b(莫比乌斯反演)

[HAOI2011]Problem b(luogu)

Description

题目描述

对于给出的 n 个询问,每次求有多少个数对 (x,y),满足 axb,cyd,且 gcd(x,y)=k ,

gcd(x,y) 函数为 x 和 y 的最大公约数。

输入格式

第一行一个整数 n,接下来 n 行每行五个整数,分别表示 a,b,c,d,k

输出格式

共 n 行,每行一个整数表示满足要求的数对 (x,y) 的个数。

Soluiton

入门资料-莫比乌斯反演讲解(里面有这道题的详细解答)

先用容斥把它分成四个 x , y 取值下界为1的子问题,然后用莫比乌斯反演

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int N=5e4,M=5e4+10;
int vis[M],u[M],a,b,c,d,k,t,prime[M],tot;
inline char get()
{
    static char buf[1024];
    static int pos=0,size=0;
    if(pos==size)
    {
        size=fread(buf,1,1024,stdin);
        pos=0;
        if(!size) return EOF;
        else return buf[pos++];
    }
    else return buf[pos++];     
}
int read()
{
    int sum=0,fh=1;
    char ch=get();
    while(!(ch>='0' && ch<='9'))
    {
        if(ch=='-') fh=-1;
        ch=get();
    }
    while(ch>='0' && ch<='9' && ch!=EOF) sum=sum*10+ch-48,ch=get();
    return sum*fh;
}
inline ll solve(int a,int b)
{
    ll ans=0;
    a/=k,b/=k;
    int boun=min(a,b);
    for(int l=1,r;l<=boun;l=r+1)
    {
        r=min(a/(a/l),b/(b/l));
        ans+=1ll*(u[r]-u[l-1])*(a/l)*(b/l);
    }
    return ans;
}
int main()
{
    u[1]=1;
    for(int i=2;i<=N;i++)
    {
        if(!vis[i])
        {
            prime[++tot]=i;
            u[i]=-1;
        }
        for(int j=1;j<=tot && prime[j]<=N/i;j++)
        {
            vis[prime[j]*i]=1;
            if(i%prime[j]==0) break;
            else u[prime[j]*i]=-u[i];
        }
        u[i]+=u[i-1];
    }
    t=read();
    while(t--)
    {
        a=read(),b=read(),c=read(),d=read(),k=read();
        printf("%lld\n",solve(b,d)-solve(a-1,d)-solve(b,c-1)+solve(a-1,c-1));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hsez-cyx/p/12369027.html