莫比乌斯反演

思想:不直接求解,用一个序列把另一个序列表示出来。

定义 f(n)和g(n)是在正整数集合上的两个函数,如果有:


那么:

其中: 

,那么

,任意两个不同的为互异素数,那么

其它:

重点研究mu[]:

10以内:

1 -1 -1 0 -1 1 -1 0 0
在程序设计中的mu[i]就是上面的

与素因子快速筛相关的莫比乌斯求法:

int mu[105],pri[105],cnt;
bool vis[105];
void getmu(){
    mu[1]=1;
    for(int i=2;i<105;i++){
        if(!vis[i]) {
            pri[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt&&pri[j]*i<105;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]){
                mu[i*pri[j]]=-mu[i];
            }
            else {
                mu[i*pri[j]]=0;  //具有了相同的素因子,所以对应的mu[i]=0
                break;
            }
        }
    }
}

简短的莫比乌斯求法:

void getmu(){
    for(int i=1;i<105;i++){
        int targe=i==1?1:0;
        int delta=targe-mu[i];
        mu[i]=delta;
        for(int j=2*i;j<105;j+=i)  mu[j]+=delta;
    }
}

hdu 1695
求解 有多少个(q1,q2)=k,其中q1在(a,b)中,q2在(c,d)中,我们可以假设a=c=1
简单的解法:欧拉函数+容斥原理
分析:设B=b/k;  D=d/k
问题等价于求解有多少(p1,p2)=1,其中p1和p2分别在(1,B)和(1,D)中
我们这样设置,令D>B,对应:q2>q1,这就是欧拉函数。
当q2>B时直接容斥求之。
#include <iostream>
#include <cstdio>
using namespace std; //int : 2 + 9个0
const int N=1e5+10;
typedef long long LL;
LL phi[N],pri[N/10],cnt;
bool vis[N];
void getpri(){
    cnt=0;
    for(LL i=2;i<N;i++){
        if(!vis[i]) pri[cnt++]=i;
        for(LL j=0;j<cnt&&i*pri[j]<N;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]==0) break;
        }
    }
}
void get(){
    for(int i=1;i<N;i++) phi[i]=i;
    for(int i=2;i<N;i++){
        if(phi[i]==i){
            for(int j=i;j<N;j+=i){
                phi[j]=phi[j]/i*(i-1);
            }
        }
    }
}
LL fac[N],top;
void solve(LL n){
    top=0;
    for(int i=0;pri[i]*pri[i]<=n;i++){
        if(n%pri[i]==0){
            fac[top++]=pri[i];
            while(n%pri[i]==0) n/=pri[i];
        }
    }
    if(n>1)  fac[top++]=n;
}
int main()
{
    //freopen("cin.txt","r",stdin);
    LL t,a,b,c,d,k,ca=0;
    get();
    getpri();
    cin>>t;
    while(t--){
        scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
        if(b>d){
            b=b^d;  d=b^d;  b=b^d;
        }
        if(k==0||k>b||k>d)  {
            printf("Case %lld: 0\n",++ca);
            continue;
        }
        b=b/k;  d=d/k;
        LL ans=0;
        for(int i=1;i<=b;i++){
            ans=ans+phi[i];
        }
        for(int i=b+1;i<=d;i++){
            solve(i);
            LL temp=0;
            for(int j=1;j<(1<<top);j++){ //二进制枚举 容斥
                LL red=0,dd=1;
                for(int k=0;k<top;k++){
                    if(j&(1<<k)){
                        red++;
                        dd=dd*fac[k];
                    }
                }
                if(red&1) temp+=b/dd; //容斥原理求非互质因子
                else temp-=b/dd;
            }
            ans+=b-temp;
        }
        printf("Case %lld: %lld\n",++ca,ans);
    }
    return 0;
}

第二种解法:莫比乌斯反演
设:
f(k)是满足(q1,q2)=k的对数。
F(d)是(q1,q2)为k的倍数的对数(其中1<=x<=b,  1<=y<=d)
有:


关于反演的另一种解释(来自ACDREAMER大神):


推出:


将b和d都除以k处理后,1--d内所有的f(1)求出 ans1

1--b内所有的f(1)求出 ans2

ans2含有ans1中的重复部分((k1,k2)和(k2,k1)是一样的)

附图说明:

ans=ans1-ans2/2;

#include <iostream>
#include <cstdio>
using namespace std; //int : 2 + 9个0
const int N=1e5+10;
typedef long long LL;
LL mu[N],cnt,pri[N];
bool vis[N];
void getmu(){
    cnt=0;
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!vis[i]){
            pri[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt&&i*pri[j]<N;j++){
            if(i%pri[j]==0){
                mu[i*pri[j]]=0;
                vis[i*pri[j]]=1;
                break;
            }
            else {
                mu[i*pri[j]]=-mu[i];
                vis[i*pri[j]]=1;
            }
        }
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    LL t,a,b,c,d,k,ca=0;
    getmu();
    cin>>t;
    while(t--){
        scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
        if(b>d){
            b=b^d;  d=b^d;  b=b^d;
        }
        if(k==0||k>b||k>d)  {
            printf("Case %lld: 0\n",++ca);
            continue;
        }
        b=b/k;  d=d/k;
        LL ans1=0,ans2=0;
        for(int i=1;i<=b;i++)
            ans1=ans1+(LL)mu[i]*(b/i)*(d/i);
        for(int i=1;i<=b;i++)
            ans2=ans2+(LL)mu[i]*(b/i)*(b/i);
        printf("Case %lld: %lld\n",++ca,ans1-ans2/2);
    }
    return 0;
}

先写到这里,相信mobius登场后,很多难题会随之而来。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值