转自:http://blog.youkuaiyun.com/haha593572013/article/details/7901900
感谢上面博客的博主提供思路和代码
http://acm.hdu.edu.cn/showproblem.php?pid=4390
题目可以抽象成:给你一个 x1*a1 + x2*a2 +x3*a3 ……+xn*an 把 这 x1 + x2 + x3 + ……+xn 个元素分成n个集合,每个集合的元素都不为空的种类数
其中每个元素并不是都不相同,如果每个元素都不一样直接全排列就行了,但是有一样的也有不一样的,具体做法就是把一样的集合单独考虑,比如 x1 个 a1元素,看它能够分配到n个集合的种类数,因为元素是一样的所以是组合而不需要考虑排列,然后根据乘法法则相乘即可……
组合数学知识: m个相同的数放进n个容器中,就相当于有n-1块隔板,总的方案就是C(n-1,n+m-1)
然后就相当于依次将每种素数放入n个容器中,记录下每种素数各有多少放法,假设有tot种素数 f(i) 表示第i种素数的放法,那么如果n个数中某些数可以为1,即n个容器某些位置可以不放数,总的方案就是f(1)*f(2)*...*f(tot),然后再减去所有出现某些容器不放数的情况,可以用容斥原理做,即当前求的总方案数减去有一个容器放0的方案数,加上有两个位置放0的方案数,减去至少有三个位置放0的方案数。。。。。
再次感谢博主: http://blog.youkuaiyun.com/haha593572013/article/details/7901900
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int maxn=1010;
const int mod=1000000007;
vector<int> p;
typedef long long ll;
ll c[maxn][maxn];
int n;
void init()
{
c[0][0]=1;
for(int i=1;i<maxn;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
void find(int n)
{
for(int i=2;i*i<=n;i++)
if(n%i==0)
{
while(n%i==0) p.push_back(i),n/=i;
}
if(n!=1) p.push_back(n);
}
ll fun(int n,int part)
{
return c[n+part-1][part-1];
}
ll solve()
{
sort(p.begin(),p.end());
int sz=p.size();
/*for(int i=0;i<sz;i++)
cout<<p[i]<<" ";
cout<<endl;*/
int a[110],tot=0;
for(int i=0,j;i<sz;i=j)
{
j=i+1;
while(j<sz&&p[j]==p[i]) j++;
a[++tot]=j-i;
}
ll ans=0;
for(int i=0;i<=n;i++)
{
ll tmp=c[n][i];
for(int j=1;j<=tot;j++) tmp=tmp*fun(a[j],n-i)%mod;
if(i&1) ans=((ans-tmp)%mod+mod)%mod;
else ans=(ans+tmp)%mod;
}
return (ans%mod+mod)%mod;
}
int main()
{
//freopen("1001.in","r",stdin);
// freopen("my.out","w",stdout);
init();
int a;
while(scanf("%d",&n)==1)
{
for(int i=0;i<n;i++) scanf("%d",&a),find(a);
printf("%I64d\n",solve());
p.clear();
}
return 0;
}
还有个组合数学题目: http://blog.youkuaiyun.com/struggle_mind/article/details/7903958