「SDOI2017」数字表格
问题分析
\[\begin{aligned}
Ans&=\prod_{i=1}^n\prod_{j=1}^mf[\gcd(i,j)]\\
&=\prod_{t=1}^nf(t)^{\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=t]}\\
&=\prod_{t=1}^nf(t)^{\sum\limits_{t|d}^n\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}\\
&=\prod_{t=1}^n\prod_{t|d}f(t)^{\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}\\
&=\prod_{d=1}^n\prod_{t|d}f(t)^{\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}\\
&=\prod_{d=1}^{n}[\prod_{t|d}f(t)^{\mu(\frac{d}{t})}]^{\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}
\end{aligned}
\]
其中对\(\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=t]\)进行莫比乌斯反演。
设\(f(t)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=t]\)。设\(F(t)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m[t|\gcd(i,j)]=\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor\)。
因为\(F(t)=\sum\limits_{t|d}f(d)\),所以\(f(t)=\sum\limits_{t|d}\mu(\frac{d}{t})F(d)=\sum\limits_{t|d}\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor\)。
最后的式子中,中括号内的东西可以\(O(n\log n)\)预处理。然后对于每个询问进行整除分块。如果不考虑快速幂,时间复杂度就是\(O(T\sqrt n+n\log n)\)。
参考代码
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const LL Maxn = 1000010;
const LL N = 1000000;
const LL Mod = 1000000007;
LL Num, Prime[ Maxn ], Vis[ Maxn ], Mu[ Maxn ], F[ Maxn ], G[ Maxn ];
LL Power( LL x, LL y ) {
if( y == 0 ) return 1LL;
LL t = Power( x, y >> 1 );
t = t * t % Mod;
if( y & 1 ) t = t * x % Mod;
return t;
}
void Init() {
F[ 0 ] = 0; F[ 1 ] = 1;
for( int i = 2; i <= N; ++i )
F[ i ] = ( F[ i - 1 ] + F[ i - 2 ] ) % Mod;
Mu[ 1 ] = 1;
for( int i = 2; i <= N; ++i ) {
if( !Vis[ i ] ) {
Mu[ i ] = -1;
Prime[ ++Num ] = i;
}
for( int j = 1; j <= Num && i * Prime[ j ] <= N; ++j ) {
Vis[ i * Prime[ j ] ] = 1;
if( i % Prime[ j ] ) break;
Mu[ i *Prime[ j ] ] = -Mu[ i ];
}
}
for( int i = 0; i <= N; ++i ) G[ i ] = 1;
for( int i = 1; i <= N; ++i ) {
for( int j = i; j <= N; j += i ) {
if( Mu[ j / i ] == 1 )
G[ j ] = G[ j ] * F[ i ] % Mod;
if( Mu[ j / i ] == -1 )
G[ j ] = G[ j ] * Power( F[ i ], Mod - 2 ) % Mod;
}
}
for( int i = 1; i <= N; ++i ) G[ i ] = G[ i ] * G[ i - 1 ] % Mod;
return;
}
void Work() {
LL n, m;
LL Ans = 1;
scanf( "%lld%lld", &n, &m );
if( n > m ) swap( n, m );
for( int i = 1, j; i <= n; i = j + 1 ) {
j = min( ( n / ( n / i ) ), ( m / ( m / i ) ) );
Ans = Ans * Power( G[ j ] * Power( G[ i - 1 ], Mod - 2 ) % Mod, ( n / i ) * ( m / i ) % Mod ) % Mod;
}
printf( "%lld\n", Ans );
return;
}
int main() {
Init();
int TestCases;
scanf( "%d", &TestCases );
for( ; TestCases--; ) Work();
return 0;
}