[SDOI2015]约数个数和

题目链接

问题分析

首先有个很厉害的结论:

\[d(ij)=\sigma_0(ij)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1] \]

Prove:

\[\sigma_0(ij)=\sum\limits_{d|ij}1=\sum\limits_{d_1\mid i,d_2\mid j}1=\sum\limits_{d_1\mid i}\sum\limits_{d_2\mid j}[\gcd(\frac {i} {d_1},d_2)=1] \]

解释:

我们拆分\(d=d_1d_2\),如果直接拆为\(\sum\limits_{d_1\mid i}\sum\limits_{d_2|j}1\),那么显然会记重。那么我们令\(\gcd(\frac{i}{d_1},d_2)=1\),也就是说让\(d_1\)塞的因数尽可能多,那么就不会记重了。而等式右边与原式右侧等价,即证。

然后就有一下操作:

\[Ans=\sum_{i=1}^N\sum_{j=1}^M\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]=\sum_{x=1}^N\sum_{y=1}^M[\gcd(x,y)=1]\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor \]

然后一波莫比乌斯反演:

\[f(t)=\sum_{x=1}^N\sum_{y=1}^M[\gcd(x,y)=1]\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor\\ F(t)=\sum_{x=1}^N\sum_{y=1}^M[t|\gcd(x,y)]\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor=\sum_{x=1}^{\lfloor\frac{N}{t}\rfloor}\sum_{y=1}^{\lfloor\frac{M}{t}\rfloor}\lfloor\frac{M}{tx}\rfloor\lfloor\frac{N}{ty}\rfloor=\sum_{t|d}f(d)\\ f(t)=\sum_{t|d}\mu(\frac{d}{t})F(d) \]

所以

\[Ans=f(1)=\sum_{d}^{\min(N,M)}\mu(d)(\sum_{x=1}^{\lfloor\frac{N}{d}\rfloor}\lfloor\frac{N}{xd}\rfloor)(\sum_{y=1}^{\lfloor\frac{M}{d}\rfloor}\lfloor\frac{M}{yd}\rfloor) \]

然后我们预处理\(\sum\limits_{x=1}\limits^{t}\lfloor\frac{t}{d}\rfloor\),然后整除分块正常操作就好了。

参考代码

#include <bits/stdc++.h>
using namespace std;

const int Maxn = 50010;

void EulerSieve();
void Cal();

int main() {
    EulerSieve();
    int TestCases;
    scanf( "%d", &TestCases );
    for( ; TestCases--; )
        Cal();
    return 0;
}

int Vis[ Maxn ], Prime[ Maxn ], Num, Mu[ Maxn ], Sum[ Maxn ];
long long F[ Maxn ];

void EulerSieve() {
    Mu[ 1 ] = 1;
    for( int i = 2; i < Maxn; ++i ) {
        if( !Vis[ i ] ) {
            Prime[ ++Num ] = i;
            Mu[ i ] = -1;
        }
        for( int j = 1; j <= Num && 1ll * i * Prime[ j ] < 1ll * Maxn; ++j ) {
            Vis[ i * Prime[ j ] ] = 1;
            if( i % Prime[ j ] == 0 ) break;
            Mu[ i * Prime[ j ] ] = -Mu[ i ];
        }
    }
    for( int i = 1; i < Maxn; ++i ) Sum[ i ] = Sum[ i - 1 ] + Mu[ i ];
    for( int i = 1; i < Maxn; ++i ) 
        for( int l = 1, r; l <= i; l = r + 1 ) {
            r = i / ( i / l );
            F[ i ] += 1ll * ( r - l + 1 ) * ( i / l );
        }
    return;
}

void Cal() {
    int N, M;
    scanf( "%d%d", &N, &M );
    long long Ans = 0;
    for( int i = 1, j; i <= N && i <= M; i = j + 1 ) {
        j = min( N / ( N / i ), M / ( M / i ) );
        Ans += 1ll * ( Sum[ j ] - Sum[ i - 1 ] ) * F[ N / i ] * F[ M / i ];
    }
    printf( "%lld\n", Ans );
    return;
}

posted @ 2019-02-28 21:14  chy_2003  阅读(196)  评论(0编辑  收藏  举报