BZOJ2154,BZOJ2693 (Mobius反演)

BZOJ2154:Crash的数字表格
BZOJ2693:jzptab

  • BZOJ 2154
    n,m ,求 i=1nj=1n[lcm(i,j)]

  • 将原式适当变形得:

    i=1nj=1n[lcm(i,j)]====i=1nj=1nijgcd(i,j)di=1ndi=1mdd2ijd[gcd(i,j)==1]ddi=1ndi=1mdijx|ix|jμ(x)ddxμ(x)x2ndx(ndx+1)2mdx(mdx+1)2

有了上述公式,通过预处理 μ(x)x2 前缀和便可做到 O(nn)=O(n) 的单次询问优秀的复杂度。

  • BZOJ2693
    给多组n,m,求 i=1nj=1n[lcm(i,j)]

  • 明显,再使用刚才的算法会达到 O(n2) 的复杂度,无法接受。

不妨设 D=dx .
再次变形得

=ddxμ(x)x2ndx(ndx+1)2mdx(mdx+1)2DnD(nD+1)2mD(mD+1)2x|DDiμ(x)x2

不难发现,如果求出 F(D)=x|DDiμ(x)x2 的前缀和,单次询问将会在 O(n) 时间内完成。

对于 F(x) 函数,可以通过两种方式实现。
1.枚举法。对于每一个约数枚举其倍数。根据调和数列的性质可以知道该方法预处理时间复杂度为 O(nlogn)

2.线筛法。
首先有一个结论。
积性函数的约数和为积性函数

因为积性函数的卷积也是积性函数。

此外,当 D=d1q,qd1
枚举 x ,当 x 包含 q2μ(x)=0 此时函数无意义。
对于所有有意义的数,其 Dx 项必然扩大 q 倍, F(D)=F(d1)q

其余的情况都可以转化而来,便得到了线性筛法。

  • Code 2693
#include
using namespace std;
const int Maxn=1e7;
const int Mod=1e8+9;
typedef long long ll;

inline int read()
{
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
}

int Prime[Maxn+50],pr[Maxn+50],tot;
ll F[Maxn+50];

inline void sieve()
{
    F[1]=1;
    for(ll i=2;i<=Maxn;i++)
    {
        if(!pr[i])
        {
            pr[i]=i;
            F[i]=(i-(i*i%Mod))%Mod;
            Prime[++tot]=i;
        }
        for(int j=1;j<=tot;j++)
        {
            int k=i*Prime[j];
            if(k>Maxn)break;
            pr[k]=Prime[j];
            if(i%Prime[j]==0)
            {
                F[k]=F[i]*Prime[j]%Mod;
                break;
            }
            F[k]=F[i]*F[Prime[j]]%Mod;
        }
    }
    for(int i=1;i<=Maxn;i++)(F[i]+=F[i-1])%=Mod;
}

inline int sum(int x,int y)
{
    return (1ll*x*(x+1)/2)%Mod*((1ll*y*(y+1)/2)%Mod)%Mod;
}

int main()
{
    sieve();
    int T=read();
    while(T--)
    {
        int n=read(),m=read();
        if(n>m)swap(n,m);
        int pos,ans=0;
        for(int bg=1;bg<=n;bg=pos+1)
        {
            pos=min(n/(n/bg),m/(m/bg));
            ans=(1ll*ans+1ll*sum(n/bg,m/bg)*(F[pos]-F[bg-1])%Mod)%Mod;
        }
        printf("%d\n",(ans+Mod)%Mod);
    }
}

你可能感兴趣的:(Mobius反演)