题目分析:这题理解好累啊T . T
首先我们将所有的bi拆成质因数,然后统计所有质因数的个数。对于原题,我们可以假设左边是N个盒子,每种质因数就是一种颜色的球,每种质因数的个数就是每种颜色的球的个数,那么题目转化为求将所有球放入N个盒子中并且每个盒子至少有一个球的方案数。
方案数很明显不能直接求,但是通过容斥原理我们可以间接的求出答案。
令c[ i ][ j ]为从i个中选j个的组合数。
设一共有k种质因数,b[ i ]即第i种质因数的个数。
首先,我们先不管题目中每个盒子必须至少放一个球的限制条件,那么方案数ans即c[ n + b[1] - 1][n - 1] * c[ n + b[2] - 1][n - 1] * ... * c[ n + b[k] - 1][n - 1]。
令f ( n , k ) = c[ n + b[1] - 1][n - 1] * c[ n + b[2] - 1][n - 1] * ... * c[ n + b[k] - 1][n - 1]。
然后,我们可以先假设确定了一个盒子为空,那么在剩下的盒子中放球的方案数为c[ n ][ 1 ] * f ( n - 1 , k )。
再假设确定了两个盒子为空,那么在剩下的盒子中放球的方案数为c[ n ][ 2 ] * f ( n - 2 , k )。
......以此类推。
最后的答案ans = sum { ( -1 ) ^ i * c[ n ][ i ] * f ( n - i , k ) | 0 <= i < n }
为什么这样可以呢?其实手推一下应该还是能发现规律的:
假设在只有一种球并空了x个盒子的方案数为res,则res - c[ n ][ 1 ] * c[ n - 1 ][ x - 1 ] + c[ n ][ 2 ] * c[ n - 2 ][ x - 2 ] - ... + ( -1 ) ^ x * c[ n ][ x ] * c[ n - x ][ 0 ] = 0。
多种球一个道理。
或许可能会觉得这样不够严谨,下面还有一种证明方法,是我在别人的blog上看到的:
设f[ i ]为没有限制条件时在i个盒子内放球的方案数,g[ i ]为i个盒子都至少有一个球的方案数。
即最终要求g[ n ]。
我们假设n = 3,则:
f[ 3 ] = g[ 3 ] + c[ 3 ][ 1 ] * g[ 2 ] + c[ 3 ][ 2 ] * g[ 1 ]
f[ 2 ] = g[ 2 ] + c[ 2 ][ 1 ] * g[ 1 ]
f[ 1 ] = g[ 1 ]
化简可得:g[ 3 ] = f[ 3 ] - c[ 3 ][ 1 ] * f[ 2 ] + c[ 3 ][ 2 ] * f[ 1 ]
通过数学归纳法可以得到g[ n ] = sum { ( -1 ) ^ i * c[ n ][ i ] * f[ n - i ] | 0 <= i < n }
代码如下:
#include <map>
#include <cmath>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std ;
#define REP( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i )
#define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define REV( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define CLR( a , x ) memset ( a , x , sizeof a )
#define CPY( a , x ) memcpy ( a , x , sizeof a )
typedef long long LL ;
const int MAXN = 10005 ;
const int mod = 1e9 + 7 ;
int a[MAXN] , cnt ;
int c[400][400] ;
int b[MAXN] ;
int n ;
void fun () {
FOR ( i , 0 , 400 ) {
c[i][i] = c[i][0] = 1 ;
REP ( j , 0 , i ) {
c[i][j] = ( c[i - 1][j - 1] + c[i - 1][j] ) % mod ;
}
}
}
void divide ( int x ) {
for ( int i = 2 ; i * i <= x ; ++ i ) {
while ( x % i == 0 ) {
a[++ cnt] = i ;
x /= i ;
}
}
if ( x > 1 ) a[++ cnt] = x ;
}
int unique ( int n ) {
CLR ( b , 0 ) ;
sort ( a + 1 , a + n + 1 ) ;
a[0] = 0 ;
int cnt = 0 ;
FOR ( i , 1 , n ) {
if ( a[i] != a[cnt] ) a[++ cnt] = a[i] ;
++ b[cnt] ;
}
return cnt ;
}
void solve () {
int x ;
cnt = 0 ;
REP ( i , 0 , n ) {
scanf ( "%d" , &x ) ;
divide ( x ) ;
}
cnt = unique ( cnt ) ;
LL ans = 0 ;
REP ( i , 0 , n ) {
LL tmp = c[n][i] ;
FOR ( j , 1 , cnt ) tmp = ( tmp * c[n - i + b[j] - 1][n - i - 1] ) % mod ;
if ( i & 1 ) ans = ( ans - tmp + mod ) % mod ;
else ans = ( ans + tmp ) % mod ;
}
printf ( "%I64d\n" , ans ) ;
}
int main () {
fun () ;
while ( ~scanf ( "%d" , &n ) ) solve () ;
return 0 ;
}