bzoj_2301_HAOI2011_Problem b 莫比乌斯反演

首先,求x在[a,b]和y在[c,d]两区间gcd(x,y)==K的(x,y)个数,可以转化成求四次,然后容斥求

现在问题变成求[1,m]和[1,n]的(x,y)==K的个数,其实就是求[1,m/K]和[1,n/K]的(x,y)==1的个数

设F(i)=i|gcd(x,y)的(x,y)个数  f(i)=gcd(x,y)==i的个数

那么满足

$$ F(n)=\sum_{d|n}f(d)$$

再进行一波反演

$$ f(i)=\sum_{i|d}\mu(\frac{d}{i})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor$$

$$ f(i)=\sum_{d=1}^{min(n,m)}\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor\sum_{i|d}\mu(\frac{d}{i})$$

然后就可以预处理出$\mu(d)$的前缀和,求解啦

 

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=100006;

int prime[N],cnt,mu[N];
bool he[N];

void chu()
{
    mu[1]=1;
    for(int i=2;i<N;++i)
    {
        if(!he[i])
        {
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<N;++j)
        {
            he[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }

    for(int i=1;i<N;++i)
        mu[i]+=mu[i-1];
}

int Q;
int K;

int get(int n,int m)
{
    n/=K;m/=K;
    if(n>m)
        swap(n,m);
    int nx,ans=0;
    for(int i=1;i<=n;)
    {
        nx=min( n/(n/i),m/(m/i) );
        //printf("nx=%d\n",nx);
        ans+=(mu[nx]-mu[i-1])*(m/i)*(n/i);
        i=nx+1;
    }
    //printf("n=%d m=%d ans=%d\n",n,m,ans);
    return ans;
}

int main(){

    freopen("in.in","r",stdin);

    chu();

    scanf("%d",&Q);
    int l0,r0,l1,r1;
    for(int i=1;i<=Q;++i)
    {
        //printf("i=%d\n",i);
        scanf("%d%d%d%d%d",&l0,&r0,&l1,&r1,&K);
        printf("%d\n",get(r0,r1)-get(l0-1,r1)-get(l1-1,r0)+get(l0-1,l1-1));
    }

}
bzoj 2301

 

posted @ 2017-10-07 11:06  A_LEAF  阅读(42)  评论(0编辑  收藏