[蓝桥杯]堆的计数

堆的计数

题目描述

我们知道包含 NN 个元素的堆可以看成是一棵包含 NN 个节点的完全二叉树。

每个节点有一个权值。对于小根堆来说,父节点的权值一定小于其子节点的权值。

假设 NN 个节点的权值分别是 1~NN,你能求出一共有多少种不同的小根堆吗?

例如对于 NN = 4 有如下 3 种:

1

/ \

2 3

/

4

1

/ \

3 2

/

4

1

/ \

2 4

/

3

由于数量可能超过整型范围,你只需要输出结果除以 109+9109+9 的余数。

输入描述

输入输出一个整数 N (1≤N≤105)N (1≤N≤105)。

输出描述

一个整数表示答案。

输入输出样例

示例

输入

4

输出

3

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

总通过次数: 372  |  总提交次数: 543  |  通过率: 68.5%

难度: 困难   标签: 2018, 快速幂, 省赛, 动态规划

算法思路

要计算由 1~N 的 N 个不同数字组成的小根堆数量,我们需要利用组合数学和动态规划。核心思路是:

  1. ​树形结构​​:完全二叉树的结构固定,根节点为最小值 1
  2. ​子树分配​​:剩余 N-1 个数字需要分配到左右子树
  3. ​递归计算​​:每个子树也必须满足小根堆性质
  4. ​组合数学​​:通过组合数计算分配方案,利用动态规划避免重复计算
关键公式:

对于以节点 i 为根的子树:

dp[i] = C(size[i]-1, size[left]) × dp[left] × dp[right] mod MOD

其中:

  • size[i]:子树 i 的节点数
  • left/right:左右子节点
  • C(n,k):组合数,表示从 n 个元素选 k 个的方案数

算法演示

以 N=4 为例:

     1          1          1/ \        / \        / \2   3      3   2      2   4/          /          /4          4          3

三种不同的小根堆结构,均满足父节点 < 子节点的性质。

代码实现(C++)

#include <iostream>
#include <vector>
using namespace std;const long long MOD = 1000000009;
const int MAXN = 100010;long long fact[MAXN], invFact[MAXN];
int sizes[MAXN];
long long dp[MAXN];// 快速幂计算 a^b mod MOD
long long modExp(long long a, long long b) {long long res = 1;while (b) {if (b & 1) res = (res * a) % MOD;a = (a * a) % MOD;b >>= 1;}return res;
}// 预处理阶乘和逆元
void precompute(int n) {fact[0] = invFact[0] = 1;for (int i = 1; i <= n; i++) {fact[i] = fact[i-1] * i % MOD;}invFact[n] = modExp(fact[n], MOD-2);for (int i = n-1; i >= 1; i--) {invFact[i] = invFact[i+1] * (i+1) % MOD;}
}// 计算组合数 C(n,k) mod MOD
long long nCr(int n, int k) {if (k < 0 || k > n) return 0;return fact[n] * invFact[k] % MOD * invFact[n-k] % MOD;
}int main() {int N;cin >> N;// 预处理阶乘和逆元precompute(N);// 计算每个节点的子树大小for (int i = N; i >= 1; i--) {sizes[i] = 1;int left = 2*i, right = 2*i+1;if (left <= N) sizes[i] += sizes[left];if (right <= N) sizes[i] += sizes[right];}// 初始化叶子节点的dp值for (int i = 1; i <= N; i++) {int left = 2*i, right = 2*i+1;if (left > N) dp[i] = 1; // 叶子节点}// 动态规划计算dp值(从下往上)for (int i = N; i >= 1; i--) {int left = 2*i, right = 2*i+1;if (left <= N) { // 非叶子节点int left_size = sizes[left];int right_size = (right <= N) ? sizes[right] : 0;int total = sizes[i] - 1;long long comb = nCr(total, left_size);dp[i] = comb * dp[left] % MOD;if (right <= N) {dp[i] = dp[i] * dp[right] % MOD;}}}cout << dp[1] << endl;return 0;
}

算法步骤详解

  1. ​预处理阶乘和逆元​

    • 计算 0! 到 N! 的阶乘数组 fact[]
    • 通过费马小定理计算阶乘的逆元数组 invFact[](因为 MOD 是质数)
  2. ​计算子树大小​

    for (int i = N; i >= 1; i--) {sizes[i] = 1;if (2*i <= N) sizes[i] += sizes[2*i];if (2*i+1 <= N) sizes[i] += sizes[2*i+1];
    }
    • 从叶子节点向上计算每个节点的子树大小
    • 节点 i 的子树大小 = 1(自身) + 左子树大小 + 右子树大小
  3. ​初始化叶子节点​

    for (int i = 1; i <= N; i++) {if (2*i > N) dp[i] = 1; // 叶子节点方案数为1
    }
  4. ​动态规划计算方案数​

    for (int i = N; i >= 1; i--) {if (非叶子节点) {int left_size = 左子树大小;int total = sizes[i]-1; // 需要分配的总节点数long long comb = nCr(total, left_size); // 分配方案数dp[i] = comb * dp[左子节点] % MOD;dp[i] = dp[i] * dp[右子节点] % MOD; // 如果存在}
    }

实例验证

​输入 N=4 时:​

  1. 子树大小计算:

    • 节点4:size=1(叶子)
    • 节点3:size=1(叶子)
    • 节点2:size=1 + size[4] = 2
    • 节点1:size=1 + size[2] + size[3] = 4
  2. DP 计算:

    • dp[4] = 1(叶子)
    • dp[3] = 1(叶子)
    • dp[2] = C(1,1) × dp[4] = 1×1 = 1
    • dp[1] = C(3,2) × dp[2] × dp[3] = 3×1×1 = 3

​输出:3​​ ✓

注意事项

  1. ​组合数计算​

    • 使用预处理的阶乘和逆元保证 O(1) 时间复杂度
    • 组合数公式:C(n,k)=k!(n−k)!n!​modMOD
  2. ​取模运算​

    • 所有乘法操作后立即取模,防止 long long 溢出
    • 使用 (a * b) % MOD 确保中间结果不溢出
  3. ​遍历顺序​

    • ​必须从后向前遍历​​(叶子→根)
    • 确保计算 dp[i] 时子节点的 dp 值已计算完成

测试点设计

  1. ​边界值测试​

    • N=1:输出 1(单节点堆)
    • N=2:输出 1(唯一结构:根-左子)
    • N=3:输出 2(两种结构:根+左左/根+左右)
  2. ​完全二叉树测试​

    • N=7(满二叉树):输出 80
    • N=15(满二叉树):输出 21964800
  3. ​性能测试​

    • N=10^5:在 1s 内完成计算
    • 最大组合数计算:C(10^5, 50000) mod MOD
  4. ​取模验证​

    • 大 N 结果超过 long long 范围
    • 确保所有操作正确取模

优化建议

  1. ​空间优化​

    // 使用 vector 替代静态数组
    vector<long long> fact(MAXN), invFact(MAXN);
    vector<int> sizes(MAXN);
    vector<long long> dp(MAXN);
  2. ​组合数计算优化​

    // 使用卢卡斯定理处理更大的 N
    long long lucas(int n, int k) {if (k == 0) return 1;return nCr(n % MOD, k % MOD) * lucas(n / MOD, k / MOD) % MOD;
    }

  3. ​并行计算​

    // OpenMP 并行计算子树大小
    #pragma omp parallel for
    for (int i = N; i >= 1; i--) {// 计算 sizes[i]
    }

  4. ​记忆化搜索​

    // 存储已计算的子树方案数
    unordered_map<int, long long> memo;
    if (memo.count(i)) return memo[i];

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908257.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…

WebRTC中的几个Rtp*Sender

一、问题&#xff1a; webrtc当中有几个比较相似的类&#xff0c;看着都是发送RTP数据包的&#xff0c;分别是&#xff1a;RtpPacketToSend 和RtpSenderVideo还有RtpVideoSender以及RTPSender&#xff0c;这说明什么呢&#xff1f;首先&#xff0c;说明我会很多连词&#xff0…

EFI(x64)简易开发环境

文章目录 1 必须文件2 运行环境3 构建应用 (Visual Studio)4 引用 EDK2 头文件 1 必须文件 EDK2: 可以只拉取仓库本身, 不拉取其子仓库(完整构建才需要) qemu: qemu 以源码发布, QEMU for Windows – Installers (64 bit) 这里有民间构建的安装包 2 运行环境 创建一个 root …

八皇后问题深度解析

八皇后问题深度解析 一、八皇后问题的起源与背景1.1 问题起源1.2 历史发展 二、问题描述与约束条件2.1 问题描述2.2 约束条件 三、算法原理&#xff1a;回溯算法3.1 回溯算法概述3.2 八皇后问题的回溯算法实现思路 四、八皇后问题的多语言实现4.1 Python实现4.2 C实现4.3 Java实…

Cursor 工具项目构建指南: Python 3.8 环境下的 Prompt Rules 约束

简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo : 文章目录 Cursor 工具项目构建指南: Python 3.8 环境下的 Prompt Rules 约束前言项目简介技术栈…

Java中的阻塞队列

阻塞队列是什么&#xff1f; 一、阻塞队列的核心概念与特性 1.1 阻塞队列是什么&#xff1f; 简单来说&#xff0c;阻塞队列是一种特殊的队列&#xff0c;它具备普通队列先进先出&#xff08;FIFO&#xff09;的特性&#xff0c;同时还支持两个额外的重要操作&#xff1a; 当…

v1.0.1版本更新·2025年5月22日发布-优雅草星云物联网AI智控系统

v1.0.1版本更新2025年5月22日发布-优雅草星云物联网AI智控系统 开源地址 星云智控官网&#xff1a; 优雅草星云物联网AI智控软件-移动端vue: 优雅草星云物联网AI智控软件-移动端vue 星云智控PC端开源&#xff1a; 优雅草星云物联网AI智控软件-PC端vue: 优雅草星云物联网AI…

Java-IO流之转换流详解

Java-IO流之转换流详解 一、转换流概述1.1 什么是转换流1.2 转换流的作用1.3 转换流的位置 二、InputStreamReader详解2.1 基本概念2.2 构造函数2.3 核心方法2.4 使用示例&#xff1a;读取不同编码的文件 三、OutputStreamWriter详解3.1 基本概念3.2 构造函数3.3 核心方法3.4 使…

android lifeCycleOwner生命周期

一 Fragment中 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) 什么时候执行&#xff1f; 让我分析一下相关问题&#xff1a; 关于 onPause 时的数据更新: viewLifecycleOwner.lifecycleScope.launch {viewLifecycleOwner.repeatOnLifecycle(Lifecycle.Sta…

Liunx进程替换

文章目录 1.进程替换2.替换过程3.替换函数exec3.1命名解释 4.细说6个exe函数execl函数execvexeclp、execvpexecle、execve 1.进程替换 fork&#xff08;&#xff09;函数在创建子进程后&#xff0c;子进程如果想要执行一个新的程序&#xff0c;就可以使用进程的程序替换来完成…

【华为云Astro-服务编排】服务编排中图元的使用与配置

目录 子服务编排图元 子服务编排图元的作用 如何使用子服务编排图元 脚本图元 脚本图元的作用 如何使用脚本图元 记录创建图元 记录创建图元的作用 如何使用记录创建图元 记录删除图元 记录删除图元的作用 如何使用记录删除图元 记录查询图元 记录查询图元的作用…

SQL Server相关的sql语句

目录 一、数据定义语言&#xff08;DDL&#xff09;1. 创建数据库2. 修改数据库3. 删除数据库4. 创建表5. 修改表结构6. 删除表 二、数据操作语言&#xff08;DML&#xff09;1. 插入数据2. 更新数据3. 删除数据 三、数据查询语言&#xff08;DQL&#xff09;1. 基础查询2. 去重…

【Hot 100】55. 跳跃游戏

目录 引言跳跃游戏我的解题 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;【Hot 100】55. 跳跃游戏❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01; 引言 跳跃游戏 &#x…

基于51单片机的车内防窒息检测报警系统

目录 具体实现功能 设计介绍 资料内容 全部内容 资料获取 具体实现功能 具体实现功能&#xff1a; &#xff08;1&#xff09;检测车内温度及二氧化碳浓度并用lcd1602实时显示。 &#xff08;2&#xff09;当人体红外传感器检测到车内有人&#xff0c;且温度或二氧化碳浓度…

关于智能体API参考接口

关于智能体在Flask的源码&#xff1a;请求体(在payload里的是请求体)、请求头&#xff08;在headers里的i局势请求头&#xff09;。 我的例子&#xff1a; 我的疑问&#xff1a;为什么没按Coze官方API文档格式&#xff0c;在Apifox里发POST请求却能收到回复&#xff1f; 1. 你…

Excel 批量下载PDF、批量下载考勤图片——仙盟创梦IDE

在办公场景中&#xff0c;借助应用软件实现 Excel 批量处理考勤图片、电子文档与 PDF&#xff0c;具有诸多显著优势。 从考勤图片处理来看&#xff0c;通过 Excel 批量操作&#xff0c;能快速提取图片中的考勤信息&#xff0c;如员工打卡时间、面部识别数据等&#xff0c;节省…

Apache Doris + MCP:Agent 时代的实时数据分析底座

一、Apache Doris&#xff1a;面向 Agent 时代的智能数据平台 当我们谈论 2025 年时&#xff0c;业界普遍认为这将是"Agent 革命年"&#xff08;Agentic Revolution&#xff09;的开端。与传统的人机交互模式不同&#xff0c;AI Agent 作为一个全新的"用户角色…

能不能用string接收数据库的datetime类型字段

在Java中使用String类型通过MyBatis接收MySQL的datetime类型字段时&#xff0c;​可以正常工作&#xff0c;但需注意格式和潜在问题。以下是关键点&#xff1a; 1. ​直接转换是可行的​ MySQL的datetime字段&#xff08;如 2023-10-05 12:34:56&#xff09;会被MyBatis自动转…

【Python训练营打卡】day44 @浙大疏锦行

DAY 44 预训练模型 知识点回顾&#xff1a; 1. 预训练的概念 2. 常见的分类预训练模型 3. 图像预训练模型的发展史 4. 预训练的策略 5. 预训练代码实战&#xff1a;resnet18 作业&#xff1a; 1. 尝试在cifar10对比如下其他的预训练模型&#xff0c;观察差异&#xff0c;…

MySQL中关于事务和锁的常见执行命令整理包括版本区别

MySQL中关于事务和锁的常见执行命令实例整理&#xff0c;并标注了不同版本下的区别&#xff08;如MySQL 8.0与旧版本的差异&#xff09;&#xff1a; 一、事务相关命令 1. 事务控制 命令描述版本差异START TRANSACTION; 或 BEGIN;显式开启事务通用语法&#xff0c;无版本差异…