BZOJ2301: [HAOI2011]Problem b 莫比乌斯反演+容斥原理

来源:互联网 发布:java attribute用法 编辑:程序博客网 时间:2024/05/01 20:14

第一次写莫比乌斯反演,感觉这个东西真的神奇,之前就因为使用莫比乌斯函数做容斥原理就久闻莫比乌斯反演大名,今日终于大概搞懂了莫比乌斯反演,其实说白了就是两个函数满足如下条件时
这里写图片描述
有如下反演存在
这里写图片描述
这个式子就叫做莫比乌斯反演,其中u为莫比乌斯函数。
这只是一种最常用的表示的方法,还有一种表示方法在信息学竞赛中更为常用,其形式如下
这里写图片描述
这道题用到的即为这种表示方法,让我们来看看莫比乌斯反演是如何优化这道题的。
首先我们看到一个询问是两段区间,那么我们就用容斥原理将这个询问拆成四个来做,首先我们看得出求出两个数的gcd为k并不好搞,那么我们就将n,m同除以一个k,这样找区间内互质的数的个数即可,我们设一个函数f(i)表示两个数gcd为i的答案的个数,这个东西显然不太好搞,但是如果有一个函数F(i)表示i|gcd(x,y)的数对的总数的话就比较无脑了,这个值我们可以很容易的推出来应该等于(n/i)*(m/i)(下取整),那么根据刚才的第二个反演公式则有如下表达式
这里写图片描述
由于我们要找的是互质的数对的个数,所以这里的i取1,将一代入式子以后我们发现变成了一个求和的问题,但是每次都暴力求肯定是不行的,所以我们搞一些玄学的优化,我们发现(n/d)最多只有2*根号n个取值,m也同理,那么(n/d)*(m/d)也就只有根号n加根号m个取值(注意不是乘),那么我们枚举这个取值再乘上这个取值的个数即可,这个个数就是对u进行前缀和的处理,得到边界的方法也极其的简单,这样的话查询时间复杂度就被我们优化成了根号n的级别,这道题就可以水过了。

#include<cstdio>#include<cstdlib>#include<cmath>#include<ctime>#include<cstring>#include<string>#include<iostream>#include<iomanip>#include<algorithm>using namespace std;int u[100100];int zhi[100100];bool pd[100100];int top=0;void shai(){    u[1]=1;    for(int i=2;i<=100000;i++)    {        if(!pd[i]) zhi[++top]=i,u[i]=-1;        for(int j=1;j<=top && i*zhi[j]<=100000;j++)        {            pd[i*zhi[j]]=true;            if(i%zhi[j]==0) break;            u[i*zhi[j]]=-u[i];        }    }    for(int i=1;i<=100000;i++) u[i]+=u[i-1];}long long jisuan(int n,int m,int k){    n/=k;    m/=k;    int last;    long long re=0;    for(int i=1;i<=n && i<=m;i=last+1)    {        last=min(n/(n/i),m/(m/i));        re+=(long long)(n/i)*(m/i)*(u[last]-u[i-1]);    }    return re;}int main(){    int n;    scanf("%d",&n);    shai();    for(int i=1;i<=n;i++)    {        int a,b,c,d,k;        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);        printf("%lld\n",jisuan(b,d,k)+jisuan(a-1,c-1,k)-jisuan(a-1,d,k)-jisuan(b,c-1,k));    }}
0 0
原创粉丝点击