习题:Number Challenge(莫比乌斯反演)

题目

传送门

思路

三个数的情况有点麻烦

尝试将其转换为两个数

\(f(n)=\sum_{i=1}^{a}\sum_{j=1}^{b}[ij=n]\)

\(\begin{aligned}ans&=\sum_{i=1}^{ab}(f(i)\sum_{j=1}^{c}d(i*j))\\&=\sum_{i=1}^{ab}(f(i)\sum_{j=1}^{c}\sum_{u|i}\sum_{v|j}[gcd(u,v)=1])\end{aligned}\)

按照套路,交换一下枚举顺序

\(ans=\sum_{u=1}^{ab}\sum_{v=1}^{c}[gcd(u,v)=1]\sum_{i=1}^{\lfloor\frac{ab}{u}\rfloor}f(iu)\lfloor\frac{c}{v}\rfloor\)

那个gcd非常的熟悉了,顺带设\(s(n)=\sum_{i=1}^{\lfloor\frac{ab}{n}\rfloor}f(in)\)

\(ans=\sum_{i=1}^{ab}\sum_{j=1}^{c}\sum_{t|gcd(i,j)}\mu(t)s(i)\lfloor\frac{c}{j}\rfloor\)

再次交换枚举顺序

\(ans=\sum_{t=1}^{c}\mu(t)\sum_{i=1}^{\lfloor\frac{ab}{t}\rfloor}s(it)\sum_{j=1}^{\lfloor\frac{c}{t}\rfloor}\lfloor\frac{c}{jt}\rfloor\)

到此时已经能用整除分块解决,总时间复杂度为\(O(n*\sqrt {n^2}*\sqrt n)=O(n^2\sqrt n)\)

如果不用整除分块,实际上有另一种方案

实际上可以记忆化

可以用\(O(n^2)\)的时间处理出所有\(f(i)\)

再用\(O(n^2*\sqrt n)\)的时间处理出\(s(i)\)

最后\(\sum_{i=1}^{\lfloor\frac{ab}{t}\rfloor}s(it)\)可以再\(O(n^2*\sqrt n)\)的时间处理出所有情况

同理\(O(n^2)\)的时间处理出所有的\(\sum_{j=1}^{\lfloor\frac{c}{t}\rfloor}\lfloor\frac{c}{jt}\rfloor\)

之后总的时间复杂度即为\(O(n^2*\sqrt n)\)

代码

#include<iostream>
using namespace std;
const long long mod=(1ll<<30);
int a,b,c;
int mu[4000005];
int pri[2000005];
int lenp;
bool vis[4000005];
long long f[4000005];
long long s[4000005];
long long x[4000005];
long long y[4000005];
long long ans;
void oular()
{
    mu[1]=1;
    for(int i=2;i<=4000000;i++)
    {
        if(!vis[i])
        {
            pri[++lenp]=i;
            mu[i]=-1;
        }
        for(int j=1;pri[j]*i<=4000000&&j<=lenp;j++)
        {
            if(i%pri[j]==0)
            {
                mu[i*pri[j]]=0;
                vis[i*pri[j]]=1;
                break;
            }
            mu[i*pri[j]]=-mu[i];
            vis[i*pri[j]]=1;
        }
    }
}
int main()
{
    cin>>a>>b>>c;
    oular();
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
            f[i*j]++;
    for(int i=1;i<=a*b;i++)
        for(int j=1;j<=a*b/i;j++)
            s[i]=(s[i]+f[i*j])%mod;
    for(int i=1;i<=c;i++)
        for(int j=1;j<=a*b/i;j++)
            x[i]=(x[i]+s[i*j])%mod;
    for(int i=1;i<=c;i++)
        for(int j=1;j<=i;j++)
            y[i]=(y[i]+i/j)%mod;
    for(int i=1;i<=c;i++)
        ans=(ans+(mu[i]*x[i]*y[c/i]%mod+mod)%mod)%mod;
    cout<<ans;
    return 0;
}
posted @ 2020-03-30 09:32  loney_s  阅读(636)  评论(0)    收藏  举报