莫比乌斯反演入门题

先简单复习下莫比乌斯反演
莫比乌斯函数定义
莫比乌斯反演入门题_第1张图片筛法

void init()
{
    mu[1]=1;
    for(int i = 2; i<maxn; i++)
    {
        if(vis[i]==0)
        {
            vis[i]=1;
            pri[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0; j<cnt; j++)
        {
            if(1ll*pri[j]*i>=maxn)
                break;
            vis[i*pri[j]]=1;
            if(i%pri[j]==0)
            {
                mu[i*pri[j]]=0;
                break;
            }
            else
                mu[i*pri[j]]=-mu[i];
        }
    }
}

两种表达形式和性质(个人感觉形式2也就是倍数形式比较常用?)
莫比乌斯反演入门题_第2张图片
反演经常用到一个性质叫整除分块

for(int l=1,r;l<=n;l=r+1)
{
    r=n/(n/l);
    ans+=(r-l+1)*(n/l);
}

还有一些的数论结论
莫比乌斯反演入门题_第3张图片

洛谷:P3455 [POI2007]ZAP-Queries

题意::对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。

入门题,注意需要分块不然会T

#include
using namespace std;
typedef long long ll;
const int maxn=50005;
ll mu[maxn],sum[maxn];
ll pri[maxn];
ll cnt=0;
bool vis[maxn];
void init()
{
    vis[0]=vis[1]=1;
    mu[1]=1;
    sum[1]=1;
    for(int i = 2;i<maxn;i++)
    {
        if(vis[i]==0)
        {
            vis[i]=1;
            pri[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt;j++)
        {
            if(pri[j]*i>=maxn) break;
            vis[i*pri[j]]=1;
            if(i%pri[j]==0)
            {
                mu[i*pri[j]]=0;
                break;
            }
            else mu[i*pri[j]]=-mu[i];
        }

    }
    for(int i=1;i<maxn;i++) sum[i]=sum[i-1]+mu[i];

}
ll a,b,d,t,n;
int main()
{
    ios::sync_with_stdio(0);
    init();
    cin>>t;
    while(t--)
    {
        cin>>a>>b>>d;
        a/=d;b/=d;
        ll ans=0;
        for(ll i =1,r;i<=min(a,b);i=r+1)
        {
            r=min(a/(a/i),b/(b/i));
            ans+=(a/i)*(b/i)*(sum[r]-sum[i-1]);
        }
        cout<<ans<<endl;
    }
    return 0;
}

neuoj: copy and submit

题意: 求

莫比乌斯反演入门题_第4张图片

另f(x)为gcd(i,j)==x 时这个函数的值 F(x)就是gcd(i,j)==d的倍数时这个函数的值
不难发现gcd(i,j)==d的倍数 那么 i,j都是d的倍数,那么最小的i就是d 就最大i就是 ⌊ m i ⌋ \left\lfloor\frac{m}{i}\right\rfloor im*i 一共有 ⌊ m i ⌋ \left\lfloor\frac{m}{i}\right\rfloor im个,等差数列求和(a1+an)n/2 得到(i+ ⌊ m i ⌋ \left\lfloor\frac{m}{i}\right\rfloor imi) ∗ ⌊ m i ⌋ *\left\lfloor\frac{m}{i}\right\rfloor im/2 提出公因式i并且约掉分母可以得到 (1+ ⌊ m i ⌋ \left\lfloor\frac{m}{i}\right\rfloor im) ⌊ m i ⌋ \left\lfloor\frac{m}{i}\right\rfloor imi
同理 j就是(1+ ⌊ n i ⌋ \left\lfloor\frac{n}{i}\right\rfloor in)
⌊ n i ⌋ \left\lfloor\frac{n}{i}\right\rfloor ini

同理,直接循环会t对mu[i]*i^2 求前缀和

#include
using namespace std;
typedef long long ll;
const long long mod=998244353LL;
const int maxn=1e7+5;
int mu[maxn];
ll sum[maxn];
int pri[maxn];
int cnt=0;
bool vis[maxn];
void init()
{
    vis[0]=vis[1]=1;
    mu[1]=1;
    sum[0]=0;
    for(int i = 2;i<maxn;i++)
    {
        if(vis[i]==0)
        {
            vis[i]=1;
            pri[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt;j++)
        {
            if(1ll*pri[j]*i>=maxn) break;
            vis[i*pri[j]]=1;
            if(i%pri[j]==0)
            {
                mu[i*pri[j]]=0;
                break;
            }
            else mu[i*pri[j]]=-mu[i];
        }

    }
    for(ll i=1;i<maxn;i++) sum[i]=(sum[i-1]+mu[i]*i*i)%mod;

}
ll n,m,t;
int main()
{
    ios::sync_with_stdio(0);
    init();
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        ll ans=0;
        for(ll i =1,r;i<=min(n,m);i=r+1)
        {
            r=min(m/(m/i),n/(n/i));
           ll m1=(m/i)%mod,n1=(n/i)%mod;
           ans=((((((((((1+m1))%mod*m1)%mod*(1+(n1)))%mod*n1)%mod*1)%mod*1)%mod*ll(sum[r]-sum[i-1])%mod))%mod+ans)%mod;
         //  cout<
        }
        cout<<((2*ans)%mod+mod)%mod<<endl;
    }
    return 0;
}

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