「模擬賽20211030」乘積
阿新 • • 發佈:2021-10-30
題目
給定正整數 \(n\),求 \(n!\) 在 16 進位制下去掉末尾的 0 之後的末 16 位。
對於 \(100\%\) 的資料,滿足 \(1\le n<2^{64}\)。
分析
設 \(\bold{v}_p(x)\) 表示 \(x\) 中含因子 \(p\) 的個數。
首先考慮直接計算 \(\frac{n!}{2^{\bold{v}_2(n!)}}\);在此之後,由於答案在 16 進位制下去掉了末尾 0,所以我們需要修正結果,最終的答案就應該是 \(\frac{n!}{2^{\bold{v}_2(n!)}}\times 2^{\bold{v}_2(n!)\bmod 4}\)。
類似於擴充套件盧卡斯的思想,我們可以對於 \([1,n]\)
於是,現在的問題變成了,如何快速求出:
\[f_n=\prod_{k=0}^{n-1}(2k+1) \]一個比較容易想到的思路是,使用倍增。將問題描述為多項式的形式:
\[\begin{aligned} F_n(x)&=\prod_{k=0}^{n-1}(2x+2k+1)\\ f_n&=F_n(0) \end{aligned} \]這樣,下指標的加法就可以被描述為多項式平移後卷積:
\[F_{i+j}(x)=F_i(x)\cdot F_{j}(x+i) \]但是,使用多項式難免會遇到長度的問題。一個重要的觀察則是:由於我們最終對 \(2^{64}\) 取模,而我們的 \(x\) 自帶 \(2\) 的係數,所以多項式的次數不超過 63!
因此,容易想到對於下指標倍增。之後可以根據下指標的加法,求出任意的 \(f_n\)。利用 \(f_n\) 則不難求出 \(\frac{n!}{2^{\bold{v}_2(n)}}\),再求一下 \(\bold v _2(n)\bmod 4\) 即可。
小結:
- 注意觀察有效的範圍,從而減少運算,提高效率。
程式碼
#include <cstdio> #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ ) #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- ) typedef unsigned long long ull; const int MAXN = 70; template<typename _T> void read( _T &x ) { x = 0; char s = getchar(); bool f = false; while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); } while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); } if( f ) x = -x; } template<typename _T> void write( _T x ) { if( x < 0 ) putchar( '-' ), x = -x; if( 9 < x ) write( x / 10 ); putchar( x % 10 + '0' ); } char alph[] = "0123456789ABCDEF"; ull C[MAXN][MAXN]; ull F[MAXN][MAXN]; ull G[MAXN], pw[MAXN]; ull N; const int L = 64; ull Evaluate( const ull *f, const ull x ) { ull ret = 0, cur = 1; for( int k = 0 ; k < L ; k ++, cur *= x ) ret += cur * f[k]; return ret; } void Mul( ull *ret, const ull *A, const ull *B ) { static ull tmp[MAXN] = {}; for( int i = 0 ; i < L ; i ++ ) tmp[i] = 0; for( int i = 0 ; i < L ; i ++ ) for( int j = 0 ; j < L ; j ++ ) tmp[i + j] += A[i] * B[j]; for( int i = 0 ; i < L ; i ++ ) ret[i] = tmp[i]; } void Init( const int lim = 64 ) { for( int i = 0 ; i < L ; i ++ ) { C[i][0] = C[i][i] = 1; for( int j = 1 ; j < i ; j ++ ) C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; } F[0][0] = 1, F[0][1] = 2; for( int i = 0 ; i < lim - 1 ; i ++ ) { pw[0] = 1; for( int j = 1 ; j < L ; j ++ ) pw[j] = pw[j - 1] * ( 1llu << i ); for( int j = 0 ; j < L ; j ++ ) { G[j] = 0; for( int k = j ; k < L ; k ++ ) G[j] += F[i][k] * pw[k - j] * C[k][j]; } Mul( F[i + 1], F[i], G ); } } ull Query( const ull n ) { if( n < 1 ) return 1; ull ret = 1; for( int k = 0 ; k < 64 ; k ++ ) if( n >> k & 1 ) ret *= Evaluate( F[k], n - ( ( n >> k ) << k ) ); return ret; } int main() { freopen( "multiplication.in", "r", stdin ); freopen( "multiplication.out", "w", stdout ); Init(); int T; read( T ); while( T -- ) { read( N ); ull ans = 1; for( int k = 0 ; k < 64 ; k ++ ) ans *= Query( ( ( ( N >> k ) - 1 ) >> 1 ) + 1 ); int lst = 0; for( ull x = N ; x ; ) ( lst += ( x >>= 1 ) % 4 ) %= 4; ans <<= lst; bool fir = false; for( int k = 15 ; ~ k ; k -- ) { unsigned tmp = ans >> ( k << 2 ) & 15; if( ! tmp ) fir ? putchar( '0' ) : 1 + 1 == 2; else fir = true, putchar( alph[tmp] ); } puts( "" ); } return 0; }