「BZOJ 2154」Crash的数字表格

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2154

Description

求解

A n s w e r = i = 1 n j = 1 m lcm ( i , j )

Solution

易知式子等于

i = 1 n j = 1 m i j gcd ( i , j )

枚举最大公因数 d ,显然两个数除以 d 得到的数互质

i = 1 n j = 1 m d | i , d | j , gcd ( i d , j d ) = 1 i j d

非常经典的 gcd 式子的化法

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

后半段式子中,出现了互质数对之积的和,为了让式子更简洁就把它拿出来单独计算。于是我们记

sum ( n , m ) = i = 1 n j = 1 m [ gcd ( i , j ) = 1 ]   i j

接下来对 sum ( n , m ) 进行化简。首先枚举约数,并将 [ gcd ( i , j ) = 1 ] 表示为 ϵ ( gcd ( i , j ) )

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

i = i d j = j d ,显然式子可以变为

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

观察上式,前半段可以预处理前缀和;后半段又是一个范围内数对之和,记

g ( n , m ) = i = 1 n j = 1 m i j = n ( n + 1 ) 2 × m ( m + 1 ) 2

可以 Θ ( 1 ) 求解

至此

sum ( n , m ) = d = 1 n μ ( d ) d 2 g ( n d , m d )

我们可以 n n d 数论分块求解 sum ( n , m ) 函数。

在求出 sum ( n , m ) 后,回到定义 sum 的地方,可得原式为

d = 1 n d sum ( n d , m d )

可见这又是一个可以数论分块求解的式子,本题除了推式子略显复杂、代码细节较多之外,是一道很好的莫比乌斯反演练习题!(上述过程中,默认 n m

时间复杂度

  • 两次数论分块: Θ ( n + m )
  • 线性筛求 μ ( n ) Θ ( n )

代码

#include <cstdio>
#include <algorithm>
using std::min;

const int N=1e7;
const int mod=20101009;
int n,m,mu[N+5],p[N/10+5],sum[N+5];
bool flg[N+5];

void init() {
    mu[1]=1;
    int tot=0,k=min(n,m);
    for(int i=2;i<=k;++i) {
        if(!flg[i]) p[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot&&i*p[j]<=k;++j) {
            flg[i*p[j]]=1;
            if(i%p[j]==0) {mu[i*p[j]]=0;break;}
            mu[i*p[j]]=-mu[i];
        }
    }
    for(int i=1;i<=k;++i) sum[i]=(sum[i-1]+1LL*i*i%mod*(mu[i]+mod))%mod;
}
int Sum(int x,int y) {
    return (1LL*x*(x+1)/2%mod)*(1LL*y*(y+1)/2%mod)%mod;
}
int func(int x,int y) {
    int res=0;
    for(int i=1,j;i<=min(x,y);i=j+1) {
        j=min(x/(x/i),y/(y/i));
        res=(res+1LL*(sum[j]-sum[i-1]+mod)*Sum(x/i,y/i)%mod)%mod;
    }
    return res;
}
int solve(int x,int y) {
    int res=0;
    for(int i=1,j;i<=min(x,y);i=j+1) {
        j=min(x/(x/i),y/(y/i));
        res=(res+1LL*(j-i+1)*(i+j)/2%mod*func(x/i,y/i)%mod)%mod;
    }
    return res;
}
int main() {
    scanf("%d%d",&n,&m);
    init();
    printf("%d\n",solve(n,m));
}

猜你喜欢

转载自blog.csdn.net/hydingsy/article/details/81784038
今日推荐