luogu 3768 简单的数学题 莫比乌斯反演+杜教筛

题意

给出 n,p n , p ,求

(i=1nj=1nijgcd(i,j))modp ( ∑ i = 1 n ∑ j = 1 n i j g c d ( i , j ) ) mod p

n1010 n ≤ 10 10

分析

不多说,直接上式子

i=1nj=1nijgcd(i,j) ∑ i = 1 n ∑ j = 1 n i j g c d ( i , j )

=d=1nd3i=1ndj=1ndij[gcd(i,j)=1] = ∑ d = 1 n d 3 ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ i j [ g c d ( i , j ) = 1 ]

反演一下可得
=d=1nd3k=1ndμ(k)k2S2(ndk) = ∑ d = 1 n d 3 ∑ k = 1 ⌊ n d ⌋ μ ( k ) k 2 S 2 ( ⌊ n d k ⌋ )

其中 S(n)=n(n+1)2 S ( n ) = n ( n + 1 ) 2
T=dk T = d k 可得
=T=1nS2(nT)T2d|Tdμ(Td) = ∑ T = 1 n S 2 ( ⌊ n T ⌋ ) T 2 ∑ d | T d μ ( T d )

=T=1nS2(nT)T2φ(T) = ∑ T = 1 n S 2 ( ⌊ n T ⌋ ) T 2 φ ( T )

很好,到了这步相信大家都已经忍不住要跟我一起大喊“分块大法好”了,然而到这里还没完。
我们还需要求 f(x)=T2φ(T) f ( x ) = T 2 φ ( T ) 的前缀和,不过这个用杜教筛随便搞搞就好了。
然后就做完了。

代码

#include
#include
#include
#include
#include
#include

typedef long long LL;

const int N=10000005;

int MOD,tot,prime[N],f[N];
LL n;
bool not_prime[N];
std::mapint> ma;

void get_prime(int n)
{
    f[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!not_prime[i]) prime[++tot]=i,f[i]=i-1;
        for (int j=1;j<=tot&&i*prime[j]<=n;j++)
        {
            not_prime[i*prime[j]]=1;
            if (i%prime[j]==0)
            {
                f[i*prime[j]]=f[i]*prime[j];
                break;
            }
            f[i*prime[j]]=f[i]*(prime[j]-1);
        }
    }
    for (int i=2;i<=n;i++) f[i]=(LL)f[i]*i%MOD*i%MOD+f[i-1],f[i]-=f[i]>=MOD?MOD:0;
}

int get_s(LL n)
{
    n%=MOD;
    return (LL)n*(n+1)/2%MOD;
}

int get_s2(LL n)
{
    n%=MOD;
    if ((LL)n*(n+1)%3==0) return (LL)n*(n+1)/6%MOD*(n*2+1)%MOD;
    else return (LL)(n*2+1)/3*((LL)n*(n+1)/2%MOD)%MOD;
}

int get_f(LL n)
{
    if (n<=10000000) return f[n];
    if (ma[n]) return ma[n];
    int ans=(LL)get_s(n)*get_s(n)%MOD;
    for (LL i=2,last;i<=n;i=last+1)
    {
        last=n/(n/i);
        ans+=MOD-(LL)(get_s2(last)+MOD-get_s2(i-1))*get_f(n/i)%MOD;
        ans-=ans>=MOD?MOD:0;
    }
    return ma[n]=ans;
}

int solve(LL n)
{
    int ans=0;
    for (LL i=1,last;i<=n;i=last+1)
    {
        last=n/(n/i);
        ans+=(LL)get_s(n/i)*get_s(n/i)%MOD*(get_f(last)+MOD-get_f(i-1))%MOD;
        ans-=ans>=MOD?MOD:0;
    }
    return ans;
}

int main()
{
    scanf("%d%lld",&MOD,&n);
    get_prime(10000000);
    printf("%d",solve(n));
    return 0;
}

你可能感兴趣的:(莫比乌斯反演,杜教筛)