「SDOI2015」约数个数和
设
d(x)
d
(
x
)
为
x
x
的约数个数,求
n,m<=50000
n
,
m
<=
50000
数据组数
<=50000
<=
50000
设
ij
i
j
唯一分解后为
则 ij i j 约数个数为
所以对于一个质数 p p ,它对的贡献就是
令 x x 为, y y 为所有可能的质因数排列方式,可以枚举地的求出 d(ij) d ( i j ) 的值
所以我们得到
可以看出 [1,n] [ 1 , n ] 中,一个数 x x 的倍数有个
我们发现现在在枚举同时为
x,y
x
,
y
的因数
k
k
,
我们可以转换一下,枚举的倍数
x,y
x
,
y
lim=min(n,m)
l
i
m
=
m
i
n
(
n
,
m
)
此时
x,y
x
,
y
的意义是
k
k
的多少倍,这样原来的就对应现在的
kx,ky
k
x
,
k
y
我们设
n=n/k,m=m/k
n
=
n
/
k
,
m
=
m
/
k
,交换一下可得
我们发现
这样的话,我们明显可以分块
O(nn‾√)
O
(
n
n
)
预处理出
那么
我们知道 ⌊nk⌋ ⌊ n k ⌋ 只有 n‾√ n 种结果,所以我们可以在 O(n‾√) O ( n ) 的时间内求出单次的 ans a n s
所以我们在 O(nn‾√) O ( n n ) 内做出这道题。
code:
#include <bits/stdc++.h>
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;
}
莫比乌斯函数的性质: