ZOJ Problem Set - 3405 Counting Factor Trees

本文探讨了如何通过质因数分解解决计数问题,并利用卡特兰数来计算不同树的组合数量。重点介绍了质因数分解的方法、质因数排列原理以及满二叉树个数的计算方法。

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

ZOJ Problem Set - 3405 Counting Factor Trees

题目描述:

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

题目大意:

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

解题思路: 

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

  • 质因数的分解:可以采用打素数表的方式解决。1亿的话打到 109 ,大概 32000 不到。要注意,分解时可能会出现至多一个质因数大于 109 ,此时要注意特殊处理。
  • 质因数的排列:显然当把 n 分解为n=pr11pr22......prmm形式的时候,可以想象,叶子结点从左至右不同的组合方式也将导致树的形态不同。因此有 ans=Cr1r1Cr2r1+r2Crmr1+r2+rm
  • 满二叉树的个数:因为 n 个子叶结点的满二叉树的个数为fn=f1fn1+f2fn2++fn1f1,这个递推公式描述的就是卡特兰数。而卡特兰数的递推公式也可以表示为 f(n)=Cn2nn+1

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

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

复杂度分析:

时间复杂度 O(n)
空间复杂度 O(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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值