您的位置:首页 > 其它

ZOJ Problem Set - 3405 Counting Factor Trees

2015-07-26 15:12 375 查看

ZOJ Problem Set - 3405 Counting Factor Trees

题目描述:

  题目链接:ZOJ Problem Set - 3405 Counting Factor Trees

题目大意:

  给一个数n,然后分解它的质因子,再用它的质因子重新组合成树,叶子结点(图片上绿色的部分)都为质因子,问能够组成多少颗不同的树

  

解题思路: 

  这个想要解决这个题,要考虑以下几个问题。

质因数的分解:可以采用打素数表的方式解决。1亿的话打到109−−−√\sqrt{10^9},大概3200032000不到。要注意,分解时可能会出现至多一个质因数大于109−−−√\sqrt{10^9},此时要注意特殊处理。

质因数的排列:显然当把nn分解为n=pr11∗pr22∗......∗prmmn=p_1^{r_1}*p_2^{r_2}*......*p_m^{r_m}形式的时候,可以想象,叶子结点从左至右不同的组合方式也将导致树的形态不同。因此有ans=Cr1r1∗Cr2r1+r2∗……∗Crmr1+r2+……rmans=C_{r_1}^{r_1}*C_{r_1+r_2}^{r2}*……*C_{r_1+r_2+……r_m}^{r_m}。

满二叉树的个数:因为nn个子叶结点的满二叉树的个数为fn=f1∗fn−1+f2∗fn−2+……+fn−1∗f1f_n=f_1*f_{n-1}+f_2*f_{n-2}+……+f_{n-1}*f_1,这个递推公式描述的就是卡特兰数。而卡特兰数的递推公式也可以表示为f(n)=Cn2nn+1f(n) =\frac{C_{2n}^n}{n+1}。

  综上,知道最后的答案就应该是题目所给的n进行分解质因数之后,其质因数的组合数与对应卡特兰数的乘积。

  对于卡特兰数的专题介绍:神奇的卡特兰数

复杂度分析:

时间复杂度 O(n√)O(\sqrt{n})

空间复杂度 O(n√)O(\sqrt{n})

AC代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>

using namespace std;
typedef long long LL;

vector<int>prime;
bool isprime[32005];
LL c[65][35];
LL f[35];

void init(){
memset(isprime,true,sizeof(isprime));
memset(c,0,sizeof(c));
memset(f,0,sizeof(f));
for(int i = 2; i <= 32000; i++){
if(isprime[i]){
prime.push_back(i);
for(int j = (i << 2); j <= 32000; j += i)
isprime[j] = false;
}
}
c[0][0] = 1;
for(int i = 1; i <= 62; i++){
c[i][0] = 1;
for(int j = 1; j <= i; j++)
c[i][j] = c[i-1][j-1] + c[i-1][j]; //组合恒等式
}
for(int i = 0; i <= 33;i++)
f[i] = c[2*i][i]/(i+1); //卡特兰数递推公式
}

int main(){
init();
int n;
while(scanf("%d",&n) != EOF){
int cnt = 0;
LL ans = 1;
for(int i = 0; i < prime.size(); i++){
if(prime.at(i)*prime.at(i) > n)break;
int tmp = 0;
if(n%prime.at(i) == 0){
while(n%prime.at(i) == 0){
tmp++;
n /= prime.at(i);
}
cnt += tmp;
ans *= c[cnt][tmp];
}
//if(n == 1)break;
}
if(n != 1) ans *= c[++cnt][1];
//如果最后n没有除为1,则说明其最大的质因数大于sqrt(n)
ans *= f[cnt-1];
printf("%lld\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: