莫比乌斯函数ACM

莫比乌斯入门请耐心往下看:

莫比乌斯函数ACM_第1张图片

莫比乌斯函数ACM_第2张图片

莫比乌斯函数ACM_第3张图片

莫比乌斯函数ACM_第4张图片

莫比乌斯函数ACM_第5张图片

莫比乌斯函数ACM_第6张图片



OK.现在可以开始刷题了。

莫比乌斯反演   HDU 1695 GCD

从区间[1, b]和[1,d]中分别选一个x, y,使得gcd(x, y) == k, 求满足条件的xy的对数(不区分xy的顺序)

分析:转换成求[1,b/k],[1,d/k]中gcd(x,y)==1的(x,y)对数.需要知道下面这个定理


然后把这个定理换成代码就好

#include
#include
#include
#include
#include
#include
#include
#include
#define sf(n)    scanf("%d", &n)
#define sff(a,b) scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define MT(x,i)  memset(x,i,sizeof(x))
typedef long long LL;
using namespace std;

const int MAXN = 100000;
/*//线性筛法求莫比乌斯函数
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
void Moblus()
{
    memset(check,false,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i = 2; i <= MAXN; i++)
    {
        if( !check[i] )
        {
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++)
        {
            if(i * prime[j] > MAXN) break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                break;
            }
            else
            {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
*/
int mu[MAXN+10]={0};
void getMu(){//n*logn
    for(int i=1; i<=MAXN; i++)
    {
        int target = i==1?1:0;
        int delta = target - mu[i];
        mu[i]=delta;
        for(int j=i*2; j<=MAXN; j+=i)
            mu[j]+=delta;
    }
}
int main()
{
    getMu();
    //Moblus();
    //for(int i=1; i<=50; i++) printf("%d %d\n",i,mu[i]);
    int a,b,c,d,k,T,cas=1;
    for(sf(T);T--;){
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0){printf("Case %d: 0\n",cas++);continue; }
        b/=k,d/=k;
        if(b>d) swap(b,d);
        LL ans=0,rep=0;//答案,重复个数
        for(int i=1;i<=b;i++){
            ans+=(LL)mu[i]*(b/i)*(d/i);
            rep+=(LL)mu[i]*(b/i)*(b/i);
        }
        ans=ans-rep/2;
        printf("Case %d: %I64d\n",cas++,ans);
    }
    return 0;
}


求莫比乌斯函数模板如下:两种求法,第一种更快,但代码量大。第二种代码少,但耗时久。

const int MAXN = 100000;
//线性筛法求莫比乌斯函数
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
void Moblus()
{
    memset(check,false,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i = 2; i <= MAXN; i++)
    {
        if( !check[i] ){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++)
        {
            if(i * prime[j] > MAXN) break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }else{
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
/*int mu[MAXN+10]={0};
void getMu(){   //n*logn   递推筛法
    for(int i=1; i<=MAXN; i++)
    {
        int target = i==1?1:0;
        int delta = target - mu[i];
        mu[i]=delta;
        for(int j=i*2; j<=MAXN; j+=i)
            mu[j]+=delta;
    }
}*/

1  2  3  4  5  6  7  8  9  10  11  12  13

0 -1 -1  0 -1  1 -1  0  0   1  -1   0  -1




BZOJ 2301 Problem b

 http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=37166

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

gcd(x,y)函数为 x 和 y 的最大公约数。

1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

数据量大,需要分段优化

#include
#include
#include
#define sf(n)    scanf("%d", &n)
typedef long long LL;
using namespace std;
const int MAXN = 50000+5;
bool check[MAXN+10];
int prime[MAXN+10],mu[MAXN+10],sum[MAXN+10];
void Moblus()
{
    memset(check,false,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i = 2; i <= MAXN; i++){
        if( !check[i] ){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++){
            if(i * prime[j] > MAXN) break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }
            else mu[i * prime[j]] = -mu[i];
        }
    }
}
LL calc(int n,int m)//计算[1,n]和[1,m]中gcd==1的数对。(3,2),(2,3)算两个
{
    LL ret=0;
    if(n>m) swap(n,m);
    for(int i=1,next=0;i<=n;i=next+1){
        int d1=n/i,d2=m/i;
        int next1=n/d1,next2=m/d2;
        next=min(next1,next2);
        ret+=(LL)(sum[next]-sum[i-1])*d1*d2;
    }
    return ret;
}
int main()
{
    Moblus();
    for(int i=1;i<=MAXN;i++) sum[i]=sum[i-1]+mu[i];
    int a,b,c,d,k,T;
    for(sf(T);T--;){
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        LL ans=calc(b/k,d/k)-calc((a-1)/k,d/k)-calc((c-1)/k,b/k)+calc((a-1)/k,(c-1)/k);
        printf("%lld\n",ans);
    }
    return 0;
}



SPOJ 7001. Visible Lattice Points(莫比乌斯反演)SPOJ VLATTICE

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=37193

求一个n*n*n的晶体,有多少点可以在(0,0,0)处可以直接看到。

也就是不是有点在它们之间,即它们gcd(x,y,z)==1.  加上特例:3个轴+3个平面

/*

这题是求(0,0,0)~(N,N,N)中gcd(a,b,c)=1的点的个数

显然就是莫比乌斯反演的板题了...很容易就能得出F(1) =sigma(miu(d)*(n/d)*(n/d)*(n/d))

但这求出来的是(1,1,1)~(N,N,N)中的点的个数...还要加上含0的三个平面内的点...

因为对称...所以直接加上二维上的整点数*3

最后再加上(0,0,1),(1,0,0),(0,1,0)三个点...就OK了...

*/


#include
#include
#include
#include
#include
#include
#include
#include
#define sf(n)    scanf("%d", &n)
#define sff(a,b) scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define MT(x,i)  memset(x,i,sizeof(x))
typedef long long LL;
using namespace std;

const int MAXN = 1000000;
//线性筛法求莫比乌斯函数
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
int sum[MAXN+10];
void Moblus()
{
    memset(check,false,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i = 2; i <= MAXN; i++)
    {
        if( !check[i] )
        {
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++)
        {
            if(i * prime[j] > MAXN) break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                break;
            }
            else
            {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
int main()
{
    Moblus();
    int T,n;
    for(sf(T);T--;){
        scanf("%d",&n);
        LL ans=3;//x,y,z轴上面的三点.(1,0,0) (0,1,0) (0,0,1);
        for(int i=1;i<=n;i++)
            ans+=(LL)mu[i]*(n/i)*(n/i)*(n/i+3);
            //ans+= (LL)mu[i]*(n/i)*(n/i)*(n/i)+(LL)mu[i]*(n/i)*(n/i)*3; //三维+二维*3
        printf("%lld\n",ans);
    }
    return 0;
}
/*
这题是求(0,0,0)~(N,N,N)中gcd(a,b,c)=1的点的个数
显然就是莫比乌斯反演的板题了...很容易就能得出F(1) = sigma(miu(d)*(n/d)*(n/d)*(n/d))
但这求出来的是(1,1,1)~(N,N,N)中的点的个数...还要加上含0的三个平面内的点...
因为对称...所以直接加上二维上的整点数*3
最后再加上(0,0,1),(1,0,0),(0,1,0)三个点...就OK了...
*/



你可能感兴趣的:(数论)