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

第一次写莫比乌斯反演,感觉这个东西真的神奇,之前就因为使用莫比乌斯函数做容斥原理就久闻莫比乌斯反演大名,今日终于大概搞懂了莫比乌斯反演,其实说白了就是两个函数满足如下条件时
这里写图片描述
有如下反演存在
这里写图片描述
这个式子就叫做莫比乌斯反演,其中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));
    }
}
发布了120 篇原创文章 · 获赞 4 · 访问量 4万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览