BZOJ 1101 [POI2007]Zap 莫比乌斯反演

来源:互联网 发布:网络推广视频教程 编辑:程序博客网 时间:2024/05/17 02:18

题意:链接

方法:莫比乌斯反演?

解析:

题中求的就是1<=x<=a1<=y<=b(gcd(x,y)==d)

即可转化为1<=x<=a/d1<=y<=b/d(gcd(x,y)==1)

又因为i=1nμ(i)=n==1?1:0

所以原公式可以转化为1<=x<=a/d1<=y<=b/dd|(x,y)μ(d)

因为d|(x,y)所以d|x d|y,那么我们可以把和式提前

即公式变为1<=d<=min(a/d,b/d)μ(d)1<=x<=a/dd|x1<=y<=b/dd|y

根据约数研究那道题,原公式可以进一步转化为

1<=d<=min(a/d,b/d)μ(d)[a/dd][b/dd]

接下来就可以暴力分块了!

μ(d)部分维护个前缀和,剩下的因为[a/dd][b/dd]具有单调不上升性,所以只需要计算每一块就好了。

代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 50010using namespace std;int tot,t;int a,b,d;int prime[N];bool f[N];int miu[N];int sum[N];void sieve(){    miu[1]=1;    for(int i=2;i<=50000;i++)    {        if(!f[i])        {            prime[++t]=i;            miu[i]=-1;        }        for(int j=1;j<=t&&i*prime[j]<=50000;j++)        {            f[i*prime[j]]=1;            if(i%prime[j]==0)            {                miu[i*prime[j]]=0;                break;            }else miu[i*prime[j]]=-miu[i];        }    }    for(int i=1;i<=50000;i++)    {        sum[i]=sum[i-1]+miu[i];    }}int main(){    sieve();    scanf("%d",&tot);    for(int i=1;i<=tot;i++)    {        scanf("%d%d%d",&a,&b,&d);        int a1=a/d,b1=b/d;        int x=min(a1,b1);        int pos,ans=0;        for(int i=1;i<=x;i=pos+1)        {            pos=min((a1/(a1/i)),(b1/(b1/i)));            ans+=(sum[pos]-sum[i-1])*(a1/i)*(b1/i);        }        printf("%d\n",ans);    }}
0 0
原创粉丝点击