P3312 [SDOI2014]数表
\[\sum_{i=1}^n\sum_{j=1}^m \sigma((i,j))
\]
\[\sum_{d=1}^{\min(n,m)}\sigma(d)\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)=d]
\]
\[\sum_{d=1}^{\min(n,m)}\sigma(d)\sum_{t=1}^{\frac{\min(n,m)}{d}}\mu(t) \lfloor \frac{n}{dt}\rfloor \lfloor \frac{m}{dt} \rfloor
\]
\[\sum_{T=1}^{\min(n,m)}\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor \sum_{d|T}\sigma(d)\mu(\frac{T}{d})
\]
如果没有 \(a\) 的限制 ,\(\sigma\) 与 \(\mu\) 的卷积为 \(id\)。
加入 \(a\) 的限制后,只要满足 \(\sigma (d) \le a\) 即可。
那么将询问对 \(a\) 从小到大排序,通过枚举倍数加入 \(\sigma(d)\mu(\frac{T}{d})\) 的贡献。
令 \(f(n)=\sum_{d|n} \sigma(d)\mu(\frac{T}{d})\)
我们需要单点修改 \(f(n)\) , 以及在数论分块时区间查询,可以用树状数组维护。
时间复杂度 \(\Theta(n \log^2 n+q\sqrt n \log n)\)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define uint unsigned int
#define Pair pair< int , int >
#define fir first
#define sec second
const int MAXN = 1e5 , MAXQ = 2e4;
struct BIT {
#define Lowbit( x ) ( x & -x )
uint Arr[ MAXN + 5 ];
void Update( int x , int val ) {
for( ; x <= MAXN ; x += Lowbit( x ) ) Arr[ x ] += val;
}
uint Sum( int l , int r ) {
uint Ans = 0; l --;
for( ; l ; l -= Lowbit( l ) ) Ans -= Arr[ l ];
for( ; r ; r -= Lowbit( r ) ) Ans += Arr[ r ];
return Ans;
}
}Tree;
int prn , prime[ MAXN + 5 ] , mu[ MAXN + 5 ] , sigma[ MAXN + 5 ];
Pair f[ MAXN + 5 ];
bool vis[ MAXN + 5 ];
void sieve( ) {
mu[ 1 ] = 1; sigma[ 1 ] = 1;
for( int i = 2 ; i <= MAXN ; i ++ ) {
if( !vis[ i ] ) {
prime[ ++ prn ] = i;
mu[ i ] = -1 , sigma[ i ] = i + 1;
}
for( int j = 1 ; j <= prn && 1ll * i * prime[ j ] <= MAXN ; j ++ ) {
vis[ i * prime[ j ] ] = 1;
if( i % prime[ j ] == 0 ) {
sigma[ i * prime[ j ] ] = ( prime[ j ] + 1 ) * sigma[ i ] - prime[ j ] * sigma[ i / prime[ j ] ];
break;
}
mu[ i * prime[ j ] ] = -mu[ i ];
sigma[ i * prime[ j ] ] = sigma[ i ] * sigma[ prime[ j ] ];
}
}
for( int i = 1 ; i <= MAXN ; i ++ ) f[ i ] = make_pair( sigma[ i ] , i );
sort( f + 1 , f + MAXN + 1 );
}
int t; uint Ans[ MAXN + 5 ];
struct Query {
int n , m , a , id;
bool operator < ( const Query &oth ) const {
return a < oth.a;
}
}Q[ MAXQ + 5 ];
int main( ) {
sieve( );
scanf("%d",&t);
for( int i = 1 ; i <= t ; i ++ )
scanf("%d %d %d",&Q[ i ].n,&Q[ i ].m,&Q[ i ].a) , Q[ i ].id = i;
sort( Q + 1 , Q + t + 1 );
for( int i = 1 , d = 0 ; i <= t ; i ++ ) {
for( ; d + 1 <= MAXN && f[ d + 1 ].fir <= Q[ i ].a ; d ++ )
for( int j = f[ d + 1 ].sec ; j <= MAXN ; j += f[ d + 1 ].sec )
Tree.Update( j , f[ d + 1 ].fir * mu[ j / f[ d + 1 ].sec ] );
int n = Q[ i ].n , m = Q[ i ].m;
for( int l = 1 , r ; l <= min( n , m ) ; l = r + 1 ) {
r = min( n / ( n / l ) , m / ( m / l ) );
Ans[ Q[ i ].id ] += 1u * ( n / l ) * ( m / l ) * Tree.Sum( l , r );
}
}
for( int i = 1 ; i <= t ; i ++ ) printf("%d\n", Ans[ i ] & 2147483647 );
return 0;
}