大菜鸡的数论之旅-莫比乌斯

大菜鸡的数论之路-莫比乌斯

苗神:学屁莫比乌斯啊,学了你也不会,那么菜,学什么莫比乌斯。
大菜鸡:???

公式打着太麻烦,就直接贴我自己模板里面的公式了。
在这里插入图片描述
在这里插入图片描述
证明:是时候给别人贡献一波访问量了(其实是我懒得打证明了)

莫比乌斯函数线性筛模板

const int MAXN=50000;

int v[MAXN+10];
int miu[MAXN+10];
int sum[MAXN+10];//求前缀和

void Moblus()
{
    for(int i=1;i<=MAXN;i++)
    {
        miu[i]=1;
        v[i]=0;
    }
    for(int i=2;i<=MAXN;i++)
    {
        if(v[i])
            continue;
        miu[i]=-1;
        for(int j=2*i;j<=MAXN;j+=i)
        {
            v[j]=1;
            if((j/i)%i==0)
                miu[j]=0;
            else
                miu[j]*=-1;
        }
    }
	/*记录莫比乌斯函数的前缀和
    for(int i=1;i<=MAXN;i++)
    {
        sum[i]=sum[i-1]+miu[i];
    }
	*/
}

下面是例题


BZOJ 1101: [POI2007]Zap

Description

FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。

Input

第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个正整数,分别为a,b,d。(1<=d<=a,b<=50000)

Output

对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。

Sample Input

2
4 5 2
6 4 3

Sample Output

3
2

Hint

对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(6,3),(3,3)。


题意

RT

思路

看上面那个链接

坑点

日常分块,日常int类型转LL然后做乘法。


代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int MAXN=50000;

int v[MAXN+10];
int miu[MAXN+10];
int sum[MAXN+10];

void Moblus()
{
    for(int i=1;i<=MAXN;i++)
    {
        miu[i]=1;
        v[i]=0;
    }
    for(int i=2;i<=MAXN;i++)
    {
        if(v[i])
            continue;
        miu[i]=-1;
        for(int j=2*i;j<=MAXN;j+=i)
        {
            v[j]=1;
            if((j/i)%i==0)
                miu[j]=0;
            else
                miu[j]*=-1;
        }
    }

    for(int i=1;i<=MAXN;i++)
    {
        sum[i]=sum[i-1]+miu[i];
    }
}

int main()
{
    Moblus();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m,d;
        ll ans=0;
        scanf("%d%d%d",&n,&m,&d);
        if(n>m)
            swap(n,m);
        n/=d,m/=d;
        for(int i=1,last=1;i<=n;i=last+1)
        {
            last=min(n/(n/i),m/(m/i));
            ans+=1ll*(sum[last]-sum[i-1])*(n/i)*(m/i);
        }
        printf("%lld\n",ans);
    }
    return 0;
}


BZOJ 2301: [HAOI2011]Problem b

Description

对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

Input

第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k

Output

共n行,每行一个整数表示满足要求的数对(x,y)的个数

Sample Input

2
2 5 1 5 1
1 5 1 5 2

Sample Output

14
3

HINT

100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000


题意

RT

思路

容斥。先跟上题一样,求[1,b],[1,d]内满足的答案数,减去[1,a-1],[1,d]和[1,b],[1,c-1]内的答案数,由于[1,a-1],[1,c-1]内的答案数被减了两次,所以我们加一次回去。即可求出[a,b],[c,d]内的答案数。

坑点

因为要求的是[a,b],[c,d]内的答案数,所以减掉的是a-1和b-1的答案数,所以需要先a–,c–。
某菜鸡最开始是先a/=k,c/=k然后再a–,c–,遂自闭。


代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int MAXN=60000;

int v[MAXN+10];
int miu[MAXN+10];
int sum[MAXN+10];

void Moblus()
{
    for(int i=1;i<=MAXN;i++)
    {
        miu[i]=1;
        v[i]=0;
    }
    for(int i=2;i<=MAXN;i++)
    {
        if(v[i])
            continue;
        miu[i]=-1;
        for(int j=2*i;j<=MAXN;j+=i)
        {
            v[j]=1;
            if((j/i)%i==0)
                miu[j]=0;
            else
                miu[j]*=-1;
        }
    }

    for(int i=1;i<=MAXN;i++)
    {
        sum[i]=sum[i-1]+miu[i];
    }
}

int main()
{
    Moblus();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int a,b,c,d,k;
        ll ans=0;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        a--,c--;
        a/=k,b/=k,c/=k,d/=k;
        int tmp;
        tmp=min(b,d);
        for(int i=1,last=1;i<=tmp;i=last+1)
        {
            last=min(b/(b/i),d/(d/i));
            ans+=1ll*(sum[last]-sum[i-1])*(b/i)*(d/i);
        }
        tmp=min(a,d);
        for(int i=1,last=1;i<=tmp;i=last+1)
        {
            last=min(a/(a/i),d/(d/i));
            ans-=1ll*(sum[last]-sum[i-1])*(a/i)*(d/i);
        }
        tmp=min(b,c);
        for(int i=1,last=1;i<=tmp;i=last+1)
        {
            last=min(b/(b/i),c/(c/i));
            ans-=1ll*(sum[last]-sum[i-1])*(b/i)*(c/i);
        }
        tmp=min(a,c);
        for(int i=1,last=1;i<=tmp;i=last+1)
        {
            last=min(a/(a/i),c/(c/i));
            ans+=1ll*(sum[last]-sum[i-1])*(a/i)*(c/i);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

发布了42 篇原创文章 · 获赞 6 · 访问量 4033

猜你喜欢

转载自blog.csdn.net/qq_43383246/article/details/99185964