BZOJ 1101 莫比乌斯函数+分块

来源:互联网 发布:dotamax查不到数据 编辑:程序博客网 时间:2024/05/18 09:17

思路:
题目中的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 55555int 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);    }}

这里写图片描述

0 0
原创粉丝点击