解题报告: Codeforces 396A. On Number of Decompositions into Multipliers 组合

来源:互联网 发布:cygwin 运行linux程序 编辑:程序博客网 时间:2024/06/08 17:10

题目链接

题意:

给你n个数ai,m为这n个数的乘积,现在要把m重新拆分成n个有序数的乘积,问有多少种拆法。


思路:

第一步、对每个数进行拆分得到m的唯一分解式。

常用的拆数方法有:

①、直接对每个数x试除所有小于等于sqrt(x)的数 

②、(常用)打出小于等于sqrt(x的最大取值)的素数表,然后试除小于等于sqrt(x)的素数。

比如对于这题的数据 ai<=1e9 

如果使用方法一进行进行拆数,每个数最多需要试除sqrt(1e9)约3e4次

使用方法二进行拆数,我们先打出小于等于Sqrt(x)的素数表只有5200个,然后进行试除,效率大大提升


第二步、

分析题意可以认为是m有k个素数因子pi,每个因子有ai个

我们需要把这些因子放进n个位置中,当然每个位置上的可以什么也不放,这样代表这个位置的数为1。

可以进一步将问题分解为求每个素因子放的的方案数,结果就是每个素因子放的方案的乘积。

比如 对于m = 36 = 2^2 *  3^2的情况,对于 任一 素因子为2的放的方案 都能和 任一  素因子为3的放的方案   都能组成不同方案:

举个详细n=2的例子:

n=2表示有两个位置要放

那么素因子为2的方案有: ( 0 , 2 ) , ( 1 , 1 ) , ( 2 , 0) ,其对应的数就为 ( 1 , 4 ) , ( 2 , 2 ) , ( 4, 1) 

那么素因子为3的方案有: ( 0 , 2 ) , ( 1 , 1 ) , ( 2 , 0) ,其对应的数就为 ( 1 , 9 ) , ( 3 , 3 ) , ( 9, 1) 

可以发现每个素因子为2的方案和每个素因子为3的方案都能组成一个不同的新方案。


第三步、

现在问题就变成了将 ai 个数放进 n个位置,每个位置可以为空,但是ai个数必须放完。

这是一个经典的高中组合数学问题,可以用插板法得到答案就是C(n+ai-1,n)。

这样这道题就被完美解决了。


代码:

#include<bits/stdc++.h>const long long mod = 1e9+7;using namespace std;namespace prime_table{const int MAX_N = 1e5;int all=0;int pr[MAX_N/10+100];bool isp[MAX_N+10];long long fro[30005];long long ni[30005];inline long long qpow(long long x,long long y){  long long res =1;  while(y){    if(y&1){      res = res  * x %mod;    }y>>=1;    x = x * x % mod;  }return res;}inline void init(){  fro[0]=ni[0]=1;  for(int i=1;i<3e4;i++){    fro[i] = fro[i-1] * i %mod;    ni[i] = qpow(fro[i],mod-2);  }    all = 0;    memset(isp,0,sizeof(isp));    for(int i=2;i<=MAX_N;i++){        if(!isp[i]){            pr[all++] = i;        }for(int j=0;j<all;j++){            long long t = 1LL*pr[j]*i ;            if(t<=MAX_N){                isp[t] = true;                if(i%pr[j]==0)break;            }else {                break;            }        }    }return ;}}using namespace prime_table;long long C(int n,int m){  return fro[n]*ni[m]%mod*ni[n-m]%mod;}map<int,int>M;int main(){  init();  //return 0;  int n;  scanf("%d",&n);  int x,sum=0;  for(int t=1;t<=n;t++){    scanf("%d",&x);    for(int i=0;i<all&&x>1;i++){      while(x%pr[i]==0){        x/=pr[i];        M[pr[i]]++;        sum++;      }    }if(x>1)M[x]++,sum++;  }long long ans = 1;  //printf("%")  for(auto it=M.begin();it!=M.end();it++){    int t = it->second;    ans = ans * C(n+t-1,t) % mod;  }printf("%I64d\n",ans);  return 0;}







阅读全文
2 0
原创粉丝点击