题目描述
给出n个数,b
1,b
2,b
3……b
n,构造n个数,a
1,a
2,……a
n(a
i>1),使得a
1*a
2*a
3……a
n=b
1*b
2……b
n;
问一共有多少种数列a
1,a
2,……a
n满足上述条件。
输入
包含多组输入数据
每组数据第一行有1个整数n(1<=n<=20)
每组数据第 二行有n个整数第i个数表示b
i.(1<b
i<=1000000)且b
1*b
2*…*b
n <10^25)。
输出
对于每组测试数据,输出有多少种数列满足情况,结果对1e9+7取余
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
2
3 4
样例输出
4
提示
我又来诈尸了!
果然组合数学只能靠运气,这么恶心……
首先易得这道题的思路,先将所有B分解质因数,然后我们可以得出,所有质因数都可以随机分到每个A里面,所以只需要算出总方案,再减去有A为1的时候的情况即可。
这道题当然是要用容斥原理做的了……
从0个为1,1个为1,2个为1……一直到n-1个1,奇减偶加。那么算方案数的方法呢?考虑到第i次容斥时有i个1,那么我们所有的质因数只能往其他n-i个数中放。而对于每种质因数,都是一模一样的,但对于我们的n-i个A却是不相同的,也就是经典的球同盒异问题。设对于某个质因数o,一共有k个o,并设x=n-i,方案数为C(k+x-1,k)。但是这i个格子可以有C(n,i)种选择,所以处理完所有质因数后,还要乘上一个C(n,i)。而要使用这么多次C,又要分解质因数,我们就只能先刷出两个表来了,一个素数表(筛法),一个组合数表(杨辉三角)。而int太不保险了,用long long保险些……
我的方法很慢,不要在意细节……
#include<cstdio>
#include<cstring>
using namespace std;
#define p 1000000007
#define ll long long
ll n,m,ans;
ll B[25];
ll A[100005];
ll vis[1000005];
ll C[1005][1005];
ll prime[100005];
ll ksm(ll n,ll k)
{
if(k==0)
return 1;
ll o=ksm(n,k/2);
if(k&1)return o*o%p*n%p;
return o*o%p;
}
void ask(ll k)
{
for(ll i=1;prime[i]&&k>1;i++)
{
while(k%prime[i]==0)
{
k/=prime[i];
A[i]++;
}
}
}
ll sum(ll h,ll k){return C[k+h-1][h-1];}
int main()
{
C[0][0]=1;
for(ll i=0;i<=1000;i++)
for(ll j=0;j<=i;j++)
C[i+1][j]=(C[i+1][j]+C[i][j])%p,C[i+1][j+1]=(C[i+1][j+1]+C[i][j])%p;
for(ll i=2;i<=1000000;i++)
{
if(!vis[i])
{
prime[++prime[0]]=i;
for(ll j=i;j<=1000000;j+=i)
vis[j]=1;
}
}
while(~scanf("%lld",&n))
{
for(ll i=1;i<=n;i++)
{
scanf("%lld",&B[i]);
ask(B[i]);
}
for(ll i=0;i<n;i++)
{
ll tmp=C[n][i];
for(ll j=1;prime[j];j++)
tmp=tmp*sum(n-i,A[j])%p;
if(i&1)
ans=((ans-tmp)%p+p)%p;
else
ans=(ans+tmp)%p;
}
printf("%lld\n",ans);
ans=0;
memset(A,0,sizeof(A));
}
}