「SDOI2015」约数个数和

「SDOI2015」约数个数和

d(x) d ( x ) x x 的约数个数,求

i=1nj=1md(ij) ∑ i = 1 n ∑ j = 1 m d ( i j )

n,m<=50000 n , m <= 50000
数据组数 <=50000 <= 50000

——————-

ij i j 唯一分解后为

kqqik+qjkk ∏ k q k q i k + q j k

ij i j 约数个数为
k(qik+qjk+1) ∏ k ( q i k + q j k + 1 )

所以对于一个质数 p p ,它对 d(ij) d ( i j ) 的贡献就是
qi+qj+1=x=0qiy=0qj[gcd(px,py)=1] q i + q j + 1 = ∑ x = 0 q i ∑ y = 0 q j [ g c d ( p x , p y ) = 1 ]

d(ij)=k(x=0qiky=0qjk[gcd(pxk,pyk)=1]) ∴ d ( i j ) = ∏ k ( ∑ x = 0 q i k ∑ y = 0 q j k [ g c d ( p k x , p k y ) = 1 ] )

x x i i y y j j 所有可能的质因数排列方式,可以枚举地的求出 d(ij) d ( i j ) 的值
d(ij)=x|iy|j[gcd(x,y)=1] d ( i j ) = ∑ x | i ∑ y | j [ g c d ( x , y ) = 1 ]

所以我们得到

ans=i=1nj=1mx|iy|j[gcd(x,y)=1] a n s = ∑ i = 1 n ∑ j = 1 m ∑ x | i ∑ y | j [ g c d ( x , y ) = 1 ]

可以看出 [1,n] [ 1 , n ] 中,一个数 x x 的倍数有 nx ⌊ n x ⌋
ans=x=1ny=1mnxmy[gcd(x,y)=1] ∴ a n s = ∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ m y ⌋ [ g c d ( x , y ) = 1 ]

d|nμ(d)=[n=1] ∵ ∑ d | n μ ( d ) = [ n = 1 ]

ans=x=1ny=1mk|gcd(x,y)μ(k)nxmy ∴ a n s = ∑ x = 1 n ∑ y = 1 m ∑ k | g c d ( x , y ) μ ( k ) ⌊ n x ⌋ ⌊ m y ⌋

ans=x=1ny=1mk|xk|yμ(k)nxmy ∴ a n s = ∑ x = 1 n ∑ y = 1 m ∑ k | x 且 k | y μ ( k ) ⌊ n x ⌋ ⌊ m y ⌋

我们发现现在在枚举同时为 x,y x , y 的因数 k k
我们可以转换一下,枚举 k k 的倍数 x,y x , y

ans=k=1limx=1nky=1mkμ(k)nkxmky a n s = ∑ k = 1 l i m ∑ x = 1 ⌊ n k ⌋ ∑ y = 1 ⌊ m k ⌋ μ ( k ) ⌊ n k x ⌋ ⌊ m k y ⌋

lim=min(n,m) l i m = m i n ( n , m )
此时 x,y x , y 的意义是 k k 的多少倍,这样原来的 x,y x , y 就对应现在的 kx,ky k x , k y
我们设 n=n/k,m=m/k n = n / k , m = m / k ,交换一下可得

ans=k=1limμ(k)x=1ny=1mnxmy a n s = ∑ k = 1 l i m μ ( k ) ∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ m y ⌋

我们发现

x=1ny=1mnxmy=x=1nnxy=1mmy ∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ m y ⌋ = ∑ x = 1 n ⌊ n x ⌋ ∗ ∑ y = 1 m ⌊ m y ⌋

这样的话,我们明显可以分块 O(nn) O ( n n ) 预处理出

f(n)=i=1nni f ( n ) = ∑ i = 1 n ⌊ n i ⌋

那么

ans=k=1limμ(k)f(nk)f(mk) a n s = ∑ k = 1 l i m μ ( k ) f ( ⌊ n k ⌋ ) f ( ⌊ m k ⌋ )

我们知道 nk ⌊ n k ⌋ 只有 n n 种结果,所以我们可以在 O(n) O ( n ) 的时间内求出单次的 ans a n s

所以我们在 O(nn) O ( n n ) 内做出这道题。

code:

#include 
using namespace std;
typedef long long ll;
const int N = 70000;

bool is_prime[N];
int prime[N], tot = 0, mobius[N]; 
int n, m; ll f[N];

void init_mobius( int size )
{
    memset( is_prime, true, sizeof( is_prime ) );
    is_prime[1] = true; mobius[1] = 1;

    for( int i = 2; i <= size; i ++ )
    {
        if( is_prime[i] )
            prime[++tot] = i, mobius[i] = -1;
        for( int j = 1; j <= tot; j ++ )
        {
            int k = i * prime[j];
            if( k > size ) break;

            is_prime[k] = false;
            if( i % prime[j] == 0 )
                { mobius[k] = 0; break; }
            mobius[k] = -mobius[i];  
        }
    }

//  for( int i = 1; i <= 20; i ++ )
//      printf( "%d ", mobius[i] ); printf( "mobius\n" ); 

    for( int i = 2; i <= size; i ++ )
        mobius[i] += mobius[i-1];
} 

inline ll calc_f( int x )
{
    ll ret = 0; int next = 0;
    for( int i = 1; i <= x; i = next+1 )
    {
//      printf( "x:%d, n:%d\n", x, n );
        next = x/(x/i); if( next > x ) next = x;
        ret += (ll)(next - i + 1) * ( x/i );
    }
//  printf( "f(%d) = %d\n", x, ret );
    return ret;
} 

inline void solve()
{
    scanf( "%d%d", &n, &m );

    ll ans = 0; int next = 0, lim = min( n, m );
    for( int i = 1; i <= lim; i = next + 1 )
    {
        next = min( n/(n/i), m/(m/i) ); if( next > lim ) next = lim;
        ans += 1ll * f[n/i] * f[m/i] * (mobius[next] - mobius[i-1]);  

//      printf( "i:%d, %lld\n", i, ans );
    }

    printf( "%lld\n", ans );
}

int main()
{
//  freopen( "output.txt", "w", stdout );

    init_mobius( 60000 );
    for( int i = 1; i <= 60000; i ++ )
        f[i] = calc_f( i );

//  for( int i = 1; i <= 20; i ++ )
//      printf( "%d ", mobius[i] );

    int ttt; scanf( "%d", &ttt );
    while( ttt -- ) 
        solve();

    return 0;
}

莫比乌斯函数的性质:

d|nμ(d)=[n=1] ∑ d | n μ ( d ) = [ n = 1 ]

你可能感兴趣的:(组合数学,数学,数论)