BZOJ 1101 莫比乌斯函数+分块

思路:
题目中的gcd(x,y)=d (x<=a,y<=b)可以转化成
求:gcd(x,y)=1 (1<=x<=a/d 1<=y<=b/d)

设 G(x,y)表示x<=a y<=b x,y互质 的数有多少组

F(a,b,k)表示有多少组x<=a y<=b gcd(x,y)>=k(注意是大于等于K)
这个很好求啊 就是(a/k)*(b/k)

G(x,y)=P1*F(a,b,1)+P2*F(a,b,2)+P3*F(a,b,3)+….+Px*F(a,b,x)

Ans=G(x,y)=ΣPx*(a/x)*(b/x)

这不就是个容斥原理嘛!
P就是莫比乌斯函数啊~
莫比乌斯函数是可以在线性时间内筛出来的~
这样我们就得到了预处理O(n)单词询问O(n/d)的方法
但是如果n很大 d很小 这个就是单词O(n)的了
怎么办呢
观察 这个式子ΣPx*(a/x)*(b/x) a/x的取值有sqrt(n)种
b/x的取值有sqrt(n)种
但是它们乘起来却不是O(n)种 而是sqrt(n)的!
预处理μ(x) 分块 求一下 完事~

//By SiriusRen
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 55555
int n,a,b,d,ans,pos,tot,mu[maxn],prime[maxn],sum[maxn],vis[maxn];
void shai(){
    sum[1]=1;
    for(int i=2;i<maxn;i++){
        if(!vis[i])mu[i]=-1,prime[++tot]=i;
        for(int j=1;j<=tot&&i*prime[j]<maxn;j++){
            vis[i*prime[j]]=1,mu[i*prime[j]]=-mu[i];
            if(i%prime[j]==0){mu[i*prime[j]]=0;break;}
        }
        sum[i]=sum[i-1]+mu[i];
    }
}
int main(){
    scanf("%d",&n);
    shai();
    while(n--){
        scanf("%d%d%d",&a,&b,&d);
        if(a>b)swap(a,b);
        a/=d,b/=d,ans=0;
        for(int i=1;i<=a;i=pos+1){
            pos=min(a/(a/i),b/(b/i));
            ans+=(sum[pos]-sum[i-1])*(a/i)*(b/i);
        }
        printf("%d\n",ans);
    }
}

这里写图片描述

posted @ 2017-01-11 08:12  SiriusRen  阅读(182)  评论(0编辑  收藏  举报