【Luogu5348】密码解锁(莫比乌斯反演,数论)

【Luogu5348】密码解锁(莫比乌斯反演,数论)

题面

洛谷

题解

首先题目给定的限制是\(\sum_{n|i}a[i]=\mu(n)\),然后把这个东西反演一下,
莫比乌斯反演的式子是:\(g(n)=\sum_{n|i}f(i)\rightarrow f(n)=\sum_{n|i}g(i)\mu(\frac{i}{n})\),在这里\(\mu\)就是\(g\),而\(a\)就是\(f\)
所以我们可以得到:\(a[m]=\sum_{m|i}\mu(i)\mu(\frac{i}{m})=\sum_{i=1}^{n/m}\mu(i)\mu(im)\)
然后直接把后面拆开,得到:\(\mu(m)\sum_{i=1}^{n/m}[gcd(i,m)=1]\mu(i)^2\)
后面那一半接着拆,可以得到:
\[\begin{aligned} a[m]&=\mu(m)\sum_{i=1}^{n/m}\mu(i)^2\sum_{j|i,j|m}\mu(j)\\ &=\mu(m)\sum_{j|m}\mu(j)\sum_{j|i}^{n/m}\mu(i)^2 \end{aligned}\]
前面的\(j\)显然只有\(\sqrt m\) 个了。
后面一半枚举最小的平方因子,然后把这部分的贡献减去就行了,这部分的复杂度是\(O(\sqrt \frac{n}{m})\)
所以总的复杂度就是\(O(\sigma_0(m)\sqrt{\frac{n}{m}})\)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
inline ll read()
{
    ll x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
ll n,ans;int m,fac[100],p;;
const int N=1e6;
bool zs[N];
int mu[N],pri[N],tot;
void Sieve()
{
    mu[1]=1;
    for(int i=2;i<N;++i)
    {
        if(!zs[i])pri[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot&&i*pri[j]<N;++j)
        {
            zs[i*pri[j]]=true;
            if(i%pri[j]==0)break;
            mu[i*pri[j]]=-mu[i];
        }
    }
}
void Calc(int j,int v)
{
    int nn=n/m/j,ret=0;
    for(int i=1;i*i<=nn*j;++i)
    {
        int ii=i*i/__gcd(i*i,j);
        ret+=nn/ii*mu[i];
    }
    ans+=v*ret;
}
void dfs(int x,int j,int mu)
{
    if(x==p+1){Calc(j,mu);return;}
    dfs(x+1,j,mu);
    dfs(x+1,j*fac[x],-mu);
}
int main()
{
    int T=read();Sieve();
    while(T--)
    {
        n=read();m=read();p=ans=0;
        int x=m;bool fl=false;
        for(int i=2;i*i<=x;++i)
            if(x%i==0)
            {
                int c=0;fac[++p]=i;
                while(x%i==0)++c,x/=i;
                if(c>1){fl=true;break;}
            }
        if(fl){puts("0");continue;}
        if(x>1)fac[++p]=x;
        dfs(1,1,1);
        printf("%lld\n",ans*((p&1)?-1:1));
    }
}

转载于:https://www.cnblogs.com/cjyyb/p/10823746.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值