luogu #2522 Problem b(莫比乌斯反演)(HAOI2011)

标签:数学


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


记g(a,b,c,d)为题目所求,记f(m,n)为满足1≤x≤m,1≤y≤n,且gcd(x,y) = k的数对个数,记F(m,n)为满足1≤x≤m,1≤y≤n,且gcd(x,y)%k=0的数对个数。
首先我们不难发现, g(a,b,c,d)=f(b,d)f(a1,d)f(b,c1)+f(a1,c1) ,而f(n,m)与F(n,m)满足莫比乌斯反演。
显然地, F(n,m)=[n/k][m/k]
则有 f(n,m)=min(n/k,m/k)i=1F(n/i,m/i)μ(i)
但这样一来,复杂度还是 O(n2) ,无法满足要求
注意到 n/i 只有 n 种取值,我们预处理 μ(i) 的前缀和即可


#include
#include
#include
#include
#define N 50050
using namespace std;
typedef long long ll;
int u[N],vis[N],p[N],top=0,t,a,b,c,d,k;
ll ans;
ll solve(int n,int m)
{
    n/=k,m/=k;
    ll ret=0,last=0;
    if (n>m) swap(n,m);
    for (int i=1;i<=n;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ret+=(ll)(n/i)*(m/i)*(u[last]-u[i-1]);
    }
    return ret;
}
int main()
{
    scanf("%d",&t),memset(vis,0,sizeof(vis)),u[1]=1;
    for (int i=2;i<=N-50;i++)
    {
        if (!vis[i]) {p[++top]=i,u[i]=-1;}
        for (int j=1;j<=top&&i*p[j]<=N-50;j++)
        {
            vis[i*p[j]]=1;
            if (i%p[j]) u[i*p[j]]=u[i]*u[p[j]];else break;
        }
    }
    for (int i=2;i<=N-50;i++) u[i]+=u[i-1];
    while (t--)
    {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k),ans=solve(b,d)-solve(a-1,d)-solve(c-1,b)+solve(a-1,c-1);
        printf("%lld\n",ans);
    }
} 

你可能感兴趣的:(数学)