设 d(x) d ( x ) 为 x x 的约数个数,求
n,m<=50000 n , m <= 50000
数据组数 <=50000 <= 50000
设 ij i j 唯一分解后为
所以我们得到
我们发现现在在枚举同时为 x,y x , y 的因数 k k ,
我们可以转换一下,枚举 k k 的倍数 x,y x , 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 ,交换一下可得
我们发现
这样的话,我们明显可以分块 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
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;
}
莫比乌斯函数的性质: