bzoj4659: Lcm(第二次做)

链接

  点击查看题目

思路

上次做这题的时候用了一些玄学水过去了,这次我写了个有理有据的 O ( n + T N ) 算法,
假设n < m,根据题意写出

a n s = i = 1 n j = 1 m i j ( i , j ) μ 2 ( ( i , j ) )

= d = 1 n d μ 2 ( d ) i = 1 n d j = 1 m d i j [ ( i , j ) = 1 ]

= d = 1 n d μ 2 ( d ) i = 1 n d j = 1 m d i j x | ( i , j ) μ ( x )

= d = 1 n d μ 2 ( d ) x = 1 n d μ ( x ) x | i n d x | j m d i j

g ( x ) = i = 1 x i = x ( x + 1 ) 2
a n s = d = 1 n d μ 2 ( d ) x = 1 n d x 2 μ ( x ) g ( n d x ) g ( m d x )

这种情况下直接做是 O ( T N )
因为每次查询 n m 都在变,因此无法预处理式子后面的那一段,考虑如何把 n m 从后面拿出来,然后再通过预处理一些和 n m 无关的部分来降低每次询问的计算次数
t = d x
a n s = d = 1 n μ 2 ( d ) d | t n t 2 d μ ( t d ) g ( n t ) g ( m t )

= t = 1 n g ( n t ) g ( m t ) t d | t t d μ ( t d ) μ 2 ( d )

这里就看出来了,只要预处理后面那一坨 t d | t t d μ ( t d ) μ 2 ( d ) ,每次就可以 O ( N )
这题最麻烦的就在这里,这个东西要分好几层来看,首先令 f ( x ) = x μ ( x ) ,那么上面那一坨就等于 t ( f μ 2 ) ( t ) ,其中 是狄利克雷卷积符号。
f ( x ) 肯定是积性函数, μ 2 ( x ) 也是积性函数,因此 ( f μ 2 ) ( x ) 也是积性函数,外面那个 x 最后再乘就行。
u ( x ) = ( f μ 2 ) ( x )
现在有两种线性筛来求出 u ( x )
一种是只记录 h ( x ) ,试图用 h ( x ) 的值来推导出 h ( x p r ( x ) ) ,其中 p r ( x ) 是指 x 的最小质因子,这种只适合于求非完全积性函数和完全积性函数的狄利克雷卷积,这里两个函数都是非完全积性函数的,因此用另一种方法
第二种方法记录每个数的最小质因子 p r ( x ) 和最小质因子的幂 p ( x ) p ( x ) 的意义是对 x 不断地除以 p r ( x ) ,直到无法整除,假设做除法的次数是 q 那么 p ( x ) = p r q ( x ) 。这样可以把一个数分成互质的两部分 x p ( x ) p ( x ) ,则 h ( x ) = h ( x p ( x ) ) h ( p ( x ) ) ,但是这种方法的缺点是当 p ( x ) = x 时,无法直接求出 h ( x ) ,必须要根据不同函数的特殊性来推导 h ( x ) 的值。
p ( x ) = p r q ( x )
q = 2 时, h ( x ) = f ( 1 ) μ 2 ( p r 2 ( x ) ) + f ( p r ( x ) ) μ 2 ( p r ( x ) ) + f ( p r 2 ( x ) ) μ ( 1 ) = p r ( x )
q = 3 时, h ( x ) = f ( 1 ) μ 2 ( p r 3 ( x ) ) + + f ( p r 3 ( x ) ) μ 2 ( 1 ) = 0
q > 3 时,同理 h ( x ) = 0
这样就知道了,当 x = p r 2 ( x ) 时, h ( x ) = p r ( x ) 而当 x = p ( x ) > p r 2 ( x ) h ( x ) = 0
然后就可以线性筛了,总的时间复杂度是 O ( N + T N )

代码

//线性筛、莫比乌斯反演 
#include <cstdio>
#include <algorithm>
#define maxn 4000010
#define mod 1073741824ll
#define ll long long
using namespace std;
ll prime[maxn], g[maxn], u[maxn], pr[maxn], p[maxn];
bool mark[maxn];
void init()
{
    ll i, j, t;
    u[1]=1;
    for(i=2;i<maxn;i++)
    {
        if(!mark[i])
        {
            prime[++*prime]=i;
            pr[i]=p[i]=i;
            u[i]=(-i+1+mod)%mod;
        }
        else
        {
            u[i]=u[i/p[i]]*u[p[i]]%mod;
            if(i==pr[i]*pr[i])u[i]=(-pr[i]+mod)%mod;
        }
        for(j=1;j<=*prime and i*prime[j]<maxn;j++)
        {
            t=i*prime[j];
            mark[t]=1;
            pr[t]=prime[j];
            if(i%prime[j]==0)p[t]=p[i]*prime[j];
            else p[t]=prime[j];
            if(i%prime[j]==0)break;
        }
    }
    for(i=1;i<maxn;i++)g[i]=i*(i+1)/2%mod;
    for(i=1;i<maxn;i++)u[i]=u[i]*i%mod;
    for(i=1;i<maxn;i++)u[i]=(u[i]+u[i-1])%mod;
}
void calc(ll n, ll m)
{
    ll t, last;
    ll ans=0;
    if(n<m)swap(n,m);
    for(t=1;t<=m;t=last+1)
    {
        last=min(n/(n/t),m/(m/t));
        ans=(ans+(g[n/t]*g[m/t])%mod*(u[last]-u[t-1]+mod)%mod)%mod;
    }
    printf("%lld\n",ans);
}
int main()
{
    ll T, n, m;
    init();
    scanf("%lld",&T);
    while(T--){scanf("%lld%lld",&n,&m);calc(n,m);}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/fsahfgsadhsakndas/article/details/80711135
今日推荐