P4240 毒瘤之神的考验
经过一番推导可得:
\[\sum_{T=1}^{\min(n,m)} \left ( \sum_{d|T} \frac{d}{\varphi(d)}\mu(\frac{T}{d}) \right) \sum_{i=1}^{\lfloor \frac{n}{T} \rfloor}\varphi(iT) \sum_{j=1}^{\lfloor \frac{m}{T} \rfloor} \varphi(jT)
\]
记 \(\displaystyle f(n)=\sum_{d|T} \frac{d}{\varphi(d)} \mu(\frac{T}{d})\) ,可以 \(\mathcal O(n \ln n)\) 预处理
记 \(\displaystyle G(n,T)=\sum_{i=1}^{n} \varphi(iT)\) , \(G(n,T)\) 的状态数为 \(n \ln n\) , 同样可以 \(\mathcal O(n \ln n)\) 递推
\[\sum_{T=1}^{\min(n,m)} f(T)G(\lfloor \frac{n}{T} \rfloor,T)G(\lfloor \frac{m}{T} \rfloor,T)
\]
记 \(\displaystyle S(n,x,y)=\sum_{T=1}^n f(T) G(x,T)G(y,T)\) , 那么有:
\[S(n,x,y)=S(n-1,x,y)+f(n)G(x,n)G(y,n)
\]
对于 \(T \le \frac{N}{B}\) 的部分直接算,对于 \(T > \frac{N}{B}\) 的部分整除分块并通过 \(S\) 算。
对 \(S\) 的复杂度的分析:
首先 \(n \le N\) 且 \(x,y \le B\) , 上界为 \(nB^2\)
注意到使 \(S(n,x,y)\) 不同于 \(S(n-1,x,y)\) 的条件为 \(G(x,n) G(y,n) \not=0\) , 即 \(n \le \frac{N}{\max(x,y)}\)
那么计算 \(S\) 的复杂度即为 \(\displaystyle \sum_{i=1}^B\sum_{j=1}^B \frac{N}{\max(i,j)}=O(NB)\)
\(B\) 取 \(\sqrt n\) 可得最优复杂度。
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int MAXN = 1e5 , MAXB = 150 , Mod = 998244353 , Inf = 0x3f3f3f3f;
inline int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
inline int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
inline int Mul( int x , int y ) { return 1ll * x * y % Mod; }
int Qkpow( int x , int po ) { int p = 1; for( ; po ; po >>= 1 , x = Mul( x , x ) ) if( po & 1 ) p = Mul( p , x ); return p; }
int Inv( int x ) { return Qkpow( x , Mod - 2 ); }
int prnum , prime[ MAXN / 10 + 5 ] , phi[ MAXN + 5 ] , mu[ MAXN + 5 ] , f[ MAXN + 5 ];
bool vis[ MAXN + 5 ];
void sieve( ) {
phi[ 1 ] = 1 , mu[ 1 ] = 1;
for( int i = 2 ; i <= MAXN ; i ++ ) {
if( !vis[ i ] ) {
prime[ ++ prnum ] = i;
phi[ i ] = i - 1 , mu[ i ] = Mod - 1;
}
for( int j = 1 ; j <= prnum && 1ll * i * prime[ j ] <= MAXN ; j ++ ) {
vis[ i * prime[ j ] ] = 1;
if( i % prime[ j ] == 0 ) {
phi[ i * prime[ j ] ] = phi[ i ] * prime[ j ];
break;
}
phi[ i * prime[ j ] ] = phi[ i ] * ( prime[ j ] - 1 );
mu[ i * prime[ j ] ] = Mod - mu[ i ];
}
}
for( int i = 1 ; i <= MAXN ; i ++ )
for( int j = i ; j <= MAXN ; j += i )
f[ j ] = Add( f[ j ] , Mul( Mul( i , Inv( phi[ i ] ) ) , mu[ j / i ] ) );
}
int T , n , m , B = 100;
vector< int > G[ MAXN + 5 ] , S[ MAXB + 5 ][ MAXB + 5 ];
int main( ) {
sieve();
for( int i = 1 ; i <= MAXN ; i ++ ) G[ i ].resize( MAXN / i + 1 );
for( int i = 1 ; i <= MAXN ; i ++ ) {
G[ 1 ][ i ] = phi[ i ];
for( int j = 2 ; 1ll * i * j <= MAXN ; j ++ ) G[ j ][ i ] = Add( G[ j - 1 ][ i ] , phi[ j * i ] );
}
for( int i = 1 ; i <= B ; i ++ )
for( int j = 1 ; j <= B ; j ++ ) {
S[ i ][ j ].resize( MAXN / max( i , j ) + 1 );
for( int k = 1 ; k <= MAXN / max( i , j ) ; k ++ )
S[ i ][ j ][ k ] = Add( S[ i ][ j ][ k - 1 ] , Mul( Mul( G[ i ][ k ] , G[ j ][ k ] ) , f[ k ] ) );
}
scanf("%d",&T);
while( T -- ) {
scanf("%d %d",&n,&m);
int Ans = 0 , d = min( n , m );
for( int i = 1 ; i <= min( MAXN / B , d ) ; i ++ ) Ans = Add( Ans , Mul( f[ i ] , Mul( G[ n / i ][ i ] , G[ m / i ][ i ] ) ) );
for( int l = MAXN / B + 1 , r ; l <= d ; l = r + 1 ) {
r = min( n / ( n / l ) , m / ( m / l ) );
Ans = Add( Ans , Sub( S[ n / l ][ m / l ][ r ] , S[ n / l ][ m / l ][ l - 1 ] ) );
}
printf("%d\n", Ans );
}
return 0;
}