#HDU4390#表达式计数(第二类Stirling + 容斥)

27 篇文章 0 订阅
14 篇文章 0 订阅

[HDU4390]表达式计数

时间限制: 1 Sec  内存限制: 128 MB

题目描述

给出n个数,b1,b2,b3……bn,构造n个数,a1,a2,……an(ai>1),使得a1*a2*a3……an=b1*b2……bn;
问一共有多少种数列a1,a2,……an满足上述条件。

输入

包含多组输入数据
每组数据第一行有1个整数n(1<=n<=20)
每组数据第 二行有n个整数第i个数表示bi.(1<bi<=1000000)且b1*b2*…*bn <10^25)。

输出

对于每组测试数据,输出有多少种数列满足情况,结果对1e9+7取余

样例输入

2
3 4

样例输出

4


数据规模太大,所以只能考虑将b1*b2*....*bn分解质因数,由唯一分解定理可得,对于该数列的乘积,只有一种分解方式,即每种质因子的个数都是一定的。

对乘积分解质因子得 a[i] 表示第 i 种质因子的个数

问题可以转化成:

现有i种不同颜色的小球,每种小球有a[i]个,还有N个不同的盒子,求将所有小球装进N个盒子的方案数,盒子不能为空( i从0开始算 )

数列顺序可交换,所以对应的盒子是不相同的

如{1,2}  {2,1}是两个数列

对于第i种小球,可以看做是将a[i]个相同的小球放进N个不同的盒子中,盒子可以为空(稍后解释)

此时方案数为C[ a[i] + N - 1, N - 1 ],证明如下:(即第二类Stirling的证明

设每个盒子中装的小球数量为Xj,Xj>=0,则是求此方程的解有多少组

X1+X2+X3+....+Xn = a[i]

X1+X2+X3+....+Xn + N = a[i] + N

此时就变成了a[i]+N个小球放进N个不同盒子中,每个盒子至少装一个

使用隔板法:

在这些小球中共有a[i]+N-1个空位可以放置隔板,放置N - 1个隔板即将它们分成N组(N盒)

从a[i]+N-1个位置中选择N - 1个位置的方案数为C[ a[i]+N-1 , N-1 ]

对于一种颜色小球的一种方案,其他小球的每一种方案都可以与之相适应,所以此时i种小球的总方案数为∑C[ a[i]+ N-1 , N-1 ] (0<=i<=cnt)

但是这里有个很明显的bug,在第i种小球的方案中,有很多是有空盒子的,这本来是必须的(因为新数列中的数并不是每个数都要含有乘积中的每个质因子)

当i种小球放在一起,就会出现空盒子(一种小球都没有),这样的情况还有很多,空盒子为0~N-1个

所以要去重复

有容斥原理:A1类元素A2类元素A3类元素....An类元素个数总和=∑Ai (1<=i<=N) - ∑既是Ai类又是Aj类元素个数总和(1<=i<=j<=N) + ∑既是Ai类又是Aj类又是Ak类元素个数总和(1<=i<=j<=k<=N) - .... + [(-1)^(N-1)]*(既是A1又是A2又是A3....又是An类元素个数总和)

在此题中,Ai类指∑第 j 种颜色的小球,空了i个盒子的方案数(即 将a[j]个相同小球放进N-i-1个盒子中的方案数) 1<=i<N , 0<=j<=cnt

如:

A1类指第j种颜色小球空了1个盒子,即放置在N-1个盒子中C[a[j]+N-1, N-1-1],1<=i<N , 0<=j<=cnt

A2类指第j种颜色小球空了2个盒子,即放置在N-2个盒子中C[a[j]+N-1, N-1-2],1<=i<N , 0<=j<=cnt

....

Acnt类指第j种颜色小球空了n-1个盒子,即放置在1个盒子中C[a[j]+N-1, 0],1<=i<N , 0<=j<=cnt

最后的答案即是:总方案数-有空盒的方案数

Code:

Status Accepted
Memory 3680kB
Length 1430
Lang G++
Submitted
Shared
RemoteRunId 21030727

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;

typedef long long LL;

const int MOD = 1e9 + 7;

int N;
LL Ans;
int a[1005];
LL C[505][505];

vector<int>P;

void pre_work(){
	C[0][0] = 1;
	for(int i = 1; i <= 500; ++ 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]) % MOD;
	}
}

void solve(int cnt){//将格子是否放置质因数进行容斥
	Ans = 1LL;//因为直接根据每种质因子分进N个格子的方案中,直接相乘会造成有的格子里一个质因子也没有,而题目要求ai > 1,所以不能有格子被空着不放
	for(int i = 0; i <= cnt; ++ i)
		Ans = (Ans * C[a[i] + N - 1][N - 1]) % MOD;//每种质因子被无限制条件地分进N个格子中的方案数(可以放,可以不放)
	for(int i = 1; i < N; ++ i){//N个格子的加减容斥
		LL tmp = C[N][i];//N个格子中有i个格子空着不放的方案数
		for(int j = 0; j <= cnt; ++ j)//第j种质因子放入剩下的N - i个格子中的方案数
			tmp = (tmp * C[N + a[j] - i - 1][N - i - 1]) % MOD;
		if(i & 1)	Ans = ((Ans - tmp) % MOD + MOD) % MOD;
		else Ans = (Ans + tmp) % MOD;
	}
}

int main(){
	pre_work();
	int x;
	while(~scanf("%d", &N)){
		for(int i = 1; i <= N; ++ i){
			scanf("%d", &x);
			for(int j = 2; j * j <= x; ++ j)
				while(x % j == 0)
					x /= j, P.push_back(j);
			if(x > 1)	P.push_back(x);
		}
		sort(P.begin(), P.end());
		int cnt = 0, len = P.size();
		a[0] = 1;
		for(int i = 1; i < len; ++ i)
			if(P[i] == P[i - 1])	++ a[cnt];
			else a[++ cnt] = 1;
		solve(cnt);//一共有cnt种质因子
		printf("%lld\n", Ans);
		memset(a, 0, sizeof(a));
		P.clear();
	}
	return 0;
}






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值