Luogu P2522 [HAOI2011]Problem b

题意

\(n\) 组询问,每组给定 \(a,b,c,d,k\),求 \(\sum\limits_{i=a}^{b}\sum\limits_{j=c}^{d}[\gcd(i,j)=k]\)

\(\texttt{Data Range:}1\leq n,k\leq 5\times 10^4,1\leq a\leq b\leq 5\times 10^4,1\leq c\leq d\leq 5\times 10^4\)

题解

首先考虑二维差分一下,发现我们只需要求出类似于 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=k]\) 的东西就好了(这里的 \(n\) 不再是数据组数)

然后考虑推一下式子

\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=k]=\sum\limits_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j)=1] \]

然后发现可以莫反一下

\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=k]=\sum\limits_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum\limits_{d\mid\gcd(i,j)}\mu(d) \]

然后交换一下求和变量得到

\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=k]=\sum\limits_{d\mid\gcd(i,j)}\mu(d)\left\lfloor\frac{n}{dk}\right\rfloor\left\lfloor\frac{m}{dk}\right\rfloor \]

然后线性筛一下 \(\mu\) 的前缀和就可以整除分块做了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51;
ll ptot,n,u,v,w,x,kk;
ll np[MAXN],prime[MAXN],mu[MAXN],prefix[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline void sieve(ll limit)
{
    np[1]=mu[1]=1;
    for(register int i=2;i<=limit;i++)
    {
        if(!np[i])
        {
            prime[++ptot]=i,mu[i]=-1;
        }
        for(register int j=1;i*prime[j]<=limit;j++)
        {
            np[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(register int i=1;i<=limit;i++)
    {
        prefix[i]=prefix[i-1]+mu[i];
    }
}
inline ll calc(ll n,ll m)
{
    ll res=0;
    for(register int l=1,r;l<=min(n,m);l=r+1)
    {
        r=min(n/(n/l),m/(m/l));
        res+=(prefix[r]-prefix[l-1])*(n/l/kk)*(m/l/kk);
    }
    return res;
}
int main()
{
    n=read(),sieve(2e5+10);
    for(register int i=1;i<=n;i++)
    {
        u=read(),v=read(),w=read(),x=read(),kk=read();
        printf("%d\n",calc(v,x)-calc(u-1,x)-calc(v,w-1)+calc(u-1,w-1));
    }
}
posted @ 2020-09-17 20:37  Karry5307  阅读(194)  评论(1编辑  收藏  举报