HDU - 4390 Number Sequence 容斥定理

本文介绍了一个关于质因数分解与组合计数的问题,通过将输入序列的每个元素进行质因数分解,统计各质因数的出现次数,并结合组合数学中的分配原理来计算满足特定条件的序列数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

Given a number sequence b 1,b 2…b n
Please count how many number sequences a 1,a 2,...,a n satisfy the condition that a1*a 2*...*a n=b 1*b 2*…*b n (a i>1).

Input
The input consists of multiple test cases. 
For each test case, the first line contains an integer n(1<=n<=20). The second line contains n integers which indicate b  1, b  2,...,b  n(1<b  i<=1000000, b  1*b  2*…*b n<=10  25).
Output
For each test case, please print the answer module 1e9 + 7.
Sample Input
2
3 4
Sample Output
4

Hint
For the sample input, P=3*4=12.
Here are the number sequences that satisfy the condition:
2 6
3 4
4 3
6 2


思路:把每个质因数出现的次数统计出来,假设质数pi,出现的次数为m次,那么就相当于把m个东西放入n个容器中,但是不允许都为空,如果不管是否为空,那么选择的方法有c(n+m-1,n-1)种,然后算出总的方法数,用容斥定理减去不合法的方法数。

代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 1000000000000
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
// 0x3f3f3f3f

const LL MOD=1e9+7;
const int maxn=1e6+50;
bool isprime[maxn];
int prime[maxn],tol;
void make_prime(int n){
    for(int i=0;i<=n;i++)
        isprime[i]=true;
    isprime[0]=isprime[1]=false;
    tol=0;
    for(int i=2;i<=n;i++){
        if(isprime[i])
            prime[tol++]=i;
        for(int j=0;j<tol;j++){
            if(i*prime[j]<=n)
                isprime[i*prime[j]]=false;
            else
                break;
            if(i%prime[j]==0)
                break;
        }
    }
}
vector<int> fac;
int num[405],b[25];
LL c[1005][1005];
LL C(int n,int m){
    return c[n+m-1][n-1];
}
int main(){

    int n;
    make_prime(maxn-50);
    for(int i=0;i<=1000;i++)
        c[i][0]=c[i][i]=1;
    for(int i=1;i<=1000;i++)
        for(int j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
    while(~scanf("%d",&n)){
        fac.clear();
        for(int i=0;i<n;i++)
            scanf("%d",&b[i]);
        for(int i=0;i<n;i++){
            int x=b[i];
            for(int j=0;j<tol&&prime[j]*prime[j]<=b[i];j++){
                while(x%prime[j]==0){
                    fac.push_back(prime[j]);
                    x/=prime[j];
                }
            }
            if(x>1) fac.push_back(x);
        }
        sort(fac.begin(),fac.end());
        mm(num,0);
        int cnt=0;
        for(int i=0;i<fac.size();i++){
            if(i==0)
                num[++cnt]=1;
            else{
                if(fac[i]==fac[i-1]) num[cnt]++;
                else num[++cnt]=1;
            }
        }
        LL ans=1;
        for(int i=1;i<=cnt;i++)
            ans=(ans*C(n,num[i]))%MOD;
        for(int i=1;i<=n-1;i++){//枚举空容器的个数
            LL tmp=c[n][i];
            for(int j=1;j<=cnt;j++)
                tmp=(tmp*C(n-i,num[j]))%MOD;
//            printf("%d  %lld\n",i,tmp);
            if(i%2==1){
                ans=(ans-tmp)%MOD;
                ans=(ans+MOD)%MOD;
            }
            else
                ans=(ans+tmp)%MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值