Codeforces Round #338 (Div. 2) D. Multipliers 组合数学+费马小定理 jxust oj 2344: 又见模法师

D
jxustoj2344

思路:
n = p1 ^ k1 * p2 ^ k2 ***** pm ^ km
n所有因子的乘积为 x[1] * x[2] *** x[w] (w为因子个数) = p[1] ^ j1 * p[2] ^ j2 ***p[m]^jm
相当于将所有因子分解质因数的到的形式。
那么需要计算p[i] 的指数 ji
考虑输入的第i个质数因子 , cnt[i] 代表第i个质数的次数。

对于第 i 个质数 ,他可以跟前后的素因子共同组成某一个n的因子,组合方式共有
pre[i-1] * rear[i+1] * num ;
其中pre[i-1] 代表前i-1个素因子可以组成因子的种数,rear[i+1]代表后i+1个素因子可以组成的因子的种数,num则为第 i 个因子的可能出现次数的所有情况 ( 0 + 1 + 2 +…+ cnt[i] ) = cnt[i] * ( cnt[i] + 1 ) / 2 ;
这样可以得到任意p[i]的指数 ji
结果就是p[1] ^ j1 * p[2] ^ j2 ***p[m]^jm,虽然得到ji但是指数过大,可以用费马小定理,将指数% ( MOD -1 )
Code:

#include 
#define LL long long 
using namespace std;
const int AX = 2e5 + 666 ; 
const int MOD = 1e9 + 7 ; 
LL quick( LL a, LL b ){
	LL ans = 1LL ;
	while( b ){
		if( b & 1 ) ans = ( ans * a ) % MOD ; 
		b >>= 1 ; 
		a = ( a * a ) % MOD ; 
	}
	return ans % MOD ; 
}
int p[AX] ; 
LL cnt[AX] = {0LL}; 
LL pre[AX];
LL rear[AX];
int main(){
	int n ; 
	scanf("%d",&n) ; 
	int x ; 
	int num = 0 ; 
	for( int i = 0 ; i < n ; i++ ){
		scanf("%d",&x);
		if( !cnt[x] ) p[++num] = x ;
		cnt[x] ++ ; 
	}
	//sort( p + 1 , p + num + 1 ) ;  
	pre[0] = 1LL ;
	rear[num+1] = 1LL ;
	for( int i = 1 ; i <= num ; i++ ){
		pre[i] = ( 1LL * pre[i-1] * ( cnt[p[i]] + 1 ) ) % ( MOD - 1 ) ; 
	}
	for( int i = num ; i >= 1 ; i-- ){
		rear[i] = ( 1LL * rear[i+1] * ( cnt[p[i]] + 1 ) ) % ( MOD - 1 ) ;
	}
	LL res = 1LL ; 
	for( int i = 1 ; i <= num ; i++ ){
		LL c = cnt[p[i]] ; 
		if( !c ) continue ; 
		LL sum = pre[i-1] * rear[i+1] % ( MOD - 1 ) ;
		LL tmp = ( 1LL * c * ( c + 1 ) / 2 % ( MOD - 1 ) * sum ) % ( MOD - 1 ) ; 
		res = ( res * quick( p[i] , tmp ) ) % MOD ; 
	}
	printf("%I64d\n",res);
	return 0 ; 	
}

你可能感兴趣的:(数论)