P2522 [HAOI2011]Problem b

题意:

n个询问,在 a ≤ x ≤ b , c ≤ y ≤ d a\le x\le b, c \le y \le d axb,cyd范围内,满足gcd(x,y)=k的数对有多少个。

Solution:

反演过程
法一:

∑ x = a b ∑ y = c d [ g c d ( x , y ) = k ] = ∑ x = a b ∑ y = c d ∑ k ∣ g c d ( x , y ) [ g c d ( x k , y k ) = 1 ] = ∑ x = ⌈ a k ⌉ ⌊ b k ⌋ ∑ y = ⌈ c k ⌉ ⌊ d k ⌋ [ g c d ( x , y ) = 1 ] = ∑ x = ⌈ a k ⌉ ⌊ b k ⌋ ∑ y = ⌈ c k ⌉ ⌊ d k ⌋ ∑ d 1 ∣ g c d ( x , y ) μ ( d 1 ) = ∑ d 1 = 1 m i n ( b , d ) μ ( d 1 ) ∑ x = ⌈ a k ⌉ ⌊ b k ⌋ ∑ y = ⌈ c k ⌉ ⌊ d k ⌋ 1 = ∑ d 1 = 1 m i n ( b , d ) μ ( d 1 ) ( ⌊ b k d 1 ⌋ − ⌈ a k d 1 ⌉ + 1 ) ( ⌊ d k d 1 ⌋ − ⌈ c k d 1 ⌉ + 1 ) \sum_{x=a}^{b}\sum_{y=c}^{d}[gcd(x,y)=k] \\ =\sum_{x=a}^b\sum_{y=c}^{d}\sum_{k|gcd(x,y)}[gcd(\frac{x}{k},\frac{y}{k})=1] \\ =\sum_{x={\lceil\frac{a}{k}}\rceil}^{\lfloor\frac{b}{k}\rfloor}\sum_{y={\lceil\frac{c}{k}}\rceil}^{\lfloor\frac{d}{k}\rfloor}[gcd(x,y)=1] \\ =\sum_{x={\lceil\frac{a}{k}}\rceil}^{\lfloor\frac{b}{k}\rfloor}\sum_{y={\lceil\frac{c}{k}}\rceil}^{\lfloor\frac{d}{k}\rfloor}\sum_{d_1|gcd(x,y)}\mu(d_1) \\ =\sum_{d_1=1}^{min(b,d)}\mu(d_1)\sum_{x={\lceil\frac{a}{k}}\rceil}^{\lfloor\frac{b}{k}\rfloor}\sum_{y={\lceil\frac{c}{k}}\rceil}^{\lfloor\frac{d}{k}\rfloor} 1 \\ =\sum_{d_1=1}^{min(b,d)}\mu(d_1)(\lfloor\frac{b}{kd_1}\rfloor -\lceil\frac{a}{kd_1}\rceil+1)(\lfloor\frac{d}{kd_1}\rfloor -\lceil\frac{c}{kd_1}\rceil+1) x=aby=cd[gcd(x,y)=k]=x=aby=cdkgcd(x,y)[gcd(kx,ky)=1]=x=kakby=kckd[gcd(x,y)=1]=x=kakby=kckdd1gcd(x,y)μ(d1)=d1=1min(b,d)μ(d1)x=kakby=kckd1=d1=1min(b,d)μ(d1)(kd1bkd1a+1)(kd1dkd1c+1)

法二:

由通用的反演式子 F ( n ) = ∑ n ∣ d f ( d ) , f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) F(n)=\sum_{n|d}f(d),f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d) F(n)=ndf(d),f(n)=ndμ(nd)F(d)推出

f ( k ) = ∑ x = a b ∑ y = c d [ g c d ( x , y ) = k ] f(k)=\sum_{x=a}^{b}\sum_{y=c}^{d}[gcd(x,y)=k] f(k)=x=aby=cd[gcd(x,y)=k]
那么
F ( n ) = ∑ n ∣ k f ( k ) = ∑ n ∣ k ∑ x = a b ∑ y = c d [ g c d ( x , y ) = k ] = ∑ x = a b ∑ y = c d [ n ∣ g c d ( x , y ) ] = ( ⌊ b n ⌋ − ⌈ a n ⌉ + 1 ) ( ⌊ d n ⌋ − ⌈ c n ⌉ + 1 ) F(n)=\sum_{n|k}f(k) \\ =\sum_{n|k}\sum_{x=a}^{b}\sum_{y=c}^{d}[gcd(x,y)=k] \\ =\sum_{x=a}^{b}\sum_{y=c}^{d}[n|gcd(x,y)] \\ =(\lfloor\frac{b}{n}\rfloor -\lceil\frac{a}{n}\rceil+1)(\lfloor\frac{d}{n}\rfloor -\lceil\frac{c}{n}\rceil+1) F(n)=nkf(k)=nkx=aby=cd[gcd(x,y)=k]=x=aby=cd[ngcd(x,y)]=(nbna+1)(ndnc+1)
反过来推出(对后面的式子中有个说明n=d1*k)
f ( k ) = ∑ k ∣ n μ ( n k ) F ( n ) = ∑ k ∣ n μ ( n k ) ( ⌊ b n ⌋ − ⌈ a n ⌉ + 1 ) ( ⌊ d n ⌋ − ⌈ c n ⌉ + 1 ) = ∑ d 1 = 1 m i n ( b k , d k ) μ ( d 1 ) ( ⌊ b k d 1 ⌋ − ⌈ a k d 1 ⌉ + 1 ) ( ⌊ d k d 1 ⌋ − ⌈ c k d 1 ⌉ + 1 ) f(k)=\sum_{k|n}\mu(\frac{n}{k})F(n) \\ =\sum_{k|n}\mu(\frac{n}{k})(\lfloor\frac{b}{n}\rfloor -\lceil\frac{a}{n}\rceil+1)(\lfloor\frac{d}{n}\rfloor -\lceil\frac{c}{n}\rceil+1) \\ =\sum_{d_1=1}^{min(\frac{b}{k},\frac{d}{k})}\mu(d_1)(\lfloor\frac{b}{kd_1}\rfloor -\lceil\frac{a}{kd_1}\rceil+1)(\lfloor\frac{d}{kd_1}\rfloor -\lceil\frac{c}{kd_1}\rceil+1) f(k)=knμ(kn)F(n)=knμ(kn)(nbna+1)(ndnc+1)=d1=1min(kb,kd)μ(d1)(kd1bkd1a+1)(kd1dkd1c+1)

到这里,反演的式子也就推出来了。但是,你不会以为到这里就结束了吧。不会吧!不会吧!
当你把代码写出来,然后提交,惊喜的发现WA了(可达鸭眉头一皱,发现事情并不简单)。
若是暴力的去跑这个反演式子,你就会发现复杂度并不够优秀,TLE了,因此要对这个过程进行优化,通过分块优化这个过程。
但是只是套上一个分块,并对 μ \mu μ进行前缀求和是不够的。
要发现前缀求和的这个特点
∑ i = a b ∑ j = c d ( . . . ) = ∑ i = 1 b ∑ j = c d ( . . . ) − ∑ i = 1 a − 1 ∑ j = c d ( . . . ) = ∑ i = 1 b ∑ j = 1 d ( . . . ) − ∑ i = 1 b ∑ j = 1 c − 1 ( . . . ) − ( ∑ i = 1 a − 1 ∑ j = 1 d ( . . . ) − ∑ i = 1 a − 1 ∑ j = 1 c − 1 ( . . . ) ) = ∑ i = 1 b ∑ j = 1 d ( . . . ) − ∑ i = 1 b ∑ j = 1 c − 1 ( . . . ) − ∑ i = 1 a − 1 ∑ j = 1 d ( . . . ) + ∑ i = 1 a − 1 ∑ j = 1 c − 1 ( . . . ) \sum_{i=a}^{b}\sum_{j=c}^{d}(...) \\ =\sum_{i=1}^{b}\sum_{j=c}^{d}(...)-\sum_{i=1}^{a-1}\sum_{j=c}^{d}(...) \\=\sum_{i=1}^{b}\sum_{j=1}^{d}(...)-\sum_{i=1}^{b}\sum_{j=1}^{c-1}(...)-(\sum_{i=1}^{a-1}\sum_{j=1}^{d}(...)-\sum_{i=1}^{a-1}\sum_{j=1}^{c-1}(...)) \\ =\sum_{i=1}^{b}\sum_{j=1}^{d}(...)-\sum_{i=1}^{b}\sum_{j=1}^{c-1}(...)-\sum_{i=1}^{a-1}\sum_{j=1}^{d}(...)+\sum_{i=1}^{a-1}\sum_{j=1}^{c-1}(...) i=abj=cd(...)=i=1bj=cd(...)i=1a1j=cd(...)=i=1bj=1d(...)i=1bj=1c1(...)(i=1a1j=1d(...)i=1a1j=1c1(...))=i=1bj=1d(...)i=1bj=1c1(...)i=1a1j=1d(...)+i=1a1j=1c1(...)
以上推演过程过于复杂,容易出错,所以一般将[a,b]的范围变成[1,b]-[1,a-1],这样就不用弄清楚上下界取整的情况。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
int mu[50005];
int vis[50005];
int prime[50005],cnt=0;
int pre[50005];
void initMul()
{
   
    mu[1]=1;
    for(int i=2;i<=50000;i++)
    {
   
        if(!vis[i])
        {
   
            mu[i]=-1;
            prime[cnt++]=i;
        }
        for(int j=0;j<cnt&&1ll*i*prime[j]<=50000;j++)
        {
   
            vis[i*prime[j]]=1;
            if(i%prime[j])mu[i*prime[j]]=-mu[i];
            else {
   mu[i*prime[j]]=0;break;}
        }
    }
    for(int i=1;i<=50000;i++)pre[i]=pre[i-1]+mu[i];
}
ll calc(int a,int b)
{
   
    ll res=0;
    int M=min(a,b);
    for(int l=1,r;l<=M;l=r+1)
    {
   
        r=min(a/(a/l),b/(b/l));
        res+=1ll*(pre[r]-pre[l-1])*(a/l)*(b/l);
    }
    return res;
}
int main()
{
   
    initMul();
    int n;scanf("%d",&n);
    while(n--)
    {
   
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        ll res=calc(b/k,d/k)-calc(b/k,(c-1)/k)-calc((a-1)/k,d/k)+calc((a-1)/k,(c-1)/k);
        printf("%lld\n",res);
    }
    return 0;
}