上海计算机学会2021年8月月赛C++乙组T3前缀中位数

文章介绍了如何使用大顶堆和小顶堆数据结构来高效地计算给定整数序列中奇数个数时的中位数。通过调整堆的大小,保证大顶堆中的最大值始终小于小顶堆中的最小值,从而快速获取中位数。

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

前缀中位数

内存限制: 256 Mb时间限制: 1000 ms

题目描述

给定一个整数序列 a1​,a2​,⋯,an​,计算出前一个数,前三个数,前五个数直到前 n 个数的中位数。

所谓一些数的中位数,就是这些数字排序后位置在最中间的数。保证 n 是一个奇数。

输入格式

第一行:单个整数表示 n,保证 n 是奇数;
第二行:n 个整数表示 a1​,a2​,⋯,an​。

输出格式

共 (n−1)/2 行:第 i 行表示前 2i−1 个数字的中位数。

数据范围
  • 0≤ai​≤10^9;
  • 对于 30% 的数据,满足 1≤n≤500;
  • 对于 60% 的数据,满足 1≤n≤30,000;
  • 对于 100% 的数据,满足1≤n≤100,000。
样例数据

输入:
7
1 3 5 7 9 11 6

输出:
1
3
5
6

输入:
5
4 3 1 4 2
输出:
4
3
3
 

解析:

使用对顶堆,保证大顶堆所有数小于等于小顶堆所有数,对于奇数个数,保证大顶堆始终比小顶堆多一个数,大顶堆堆顶即为答案。 

 详见代码:

#include<bits/stdc++.h>
using namespace std;
int n;
priority_queue <int> d;//大顶堆
priority_queue <int,vector<int>,greater<int>> x;//小顶堆
int main()
{
	cin>>n;
    for (int i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        //将a加入堆内,保证大顶堆的最大数小于小顶堆的最小数
        if (d.empty()||a<=d.top()){
            d.push(a);
        }else{
            x.push(a);
        }
        //对于奇数次
        if (i%2==1){
            //调整大小顶堆内数字个数,保证大顶堆比小顶堆多一个
            if (d.size()-x.size()==3){
                int t;
                t=d.top();
                d.pop();
                x.push(t);
            }else if(d.size()<x.size()){
                int t;
                t=x.top();
                x.pop();
                d.push(t);
            }
            //大顶堆的堆顶即为中位数
            cout<<d.top()<<endl;
        }
    }
    return 0;
}

<think>首先,用户的问题是关于上海计算机学会20251C++乙组T2题目的解题思路,题目是“元音切片”。用户提供了参考引用,其中引用[1]详细描述了题目的代码和逻辑,引用[2]似乎是一些个人笔记,不相关,所以我应该忽略引用[2]。 用户指令要求: - 所有行内学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 回答结构: - 结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠,基于提供的引用。 关键点: - 题目是关于字符串处理,具体是元音和辅音的计。 - 引用[1]中的代码和描述是核心:题目要求处理字符串,将元音序列分成每k个元音一组,计算分割方案。 - 逻辑:遍历字符串,计元音;当遇到一组k个元音时,考虑与上一组之间的辅音量,分割方案基于辅音量(方案为辅音加一),使用乘法原理累乘这些方案。 - 代码中:ans 初始为1,cnt 计元音,x 计辅音,flag 标记第一组元音。 解题思路步骤: 1. **理解问题**:给定字符串,包含元音(a,e,i,o,u)和辅音。要求将元音序列分成连续的段,每段恰好k个元音。计算所有可能的分割方案2. **关键观察**:分割点只能在元音组之间,具体是每两段k元音之间的辅音序列中,可以选择任意位置作为分割点(包括两端),所以方案为辅音量加一。 3. **算法**: - 遍历字符串。 - 维护当元音计 cnt。 - 当 cnt 达到 k 时重置为0,表示完成一组。 - 在每组开始时(cnt==1),如果不是第一组,则计算与上一组之间的辅音量 x,ans 乘上 (x+1),并取模。 - 辅音计 x 只在元音组之间计。 4. **边界处理**:第一组元音不需要分割,所以用 flag 标记是否已开始第一组。 5. **复杂度**:O(n) 时间,一次遍历。 在回答中,我需要: - 用中文解释。 - 使用学表达式:例如,方案计算为 $(x + 1)$。 - 生成相关问题:在最后添加相关问题部分。 - 添加引用:由于引用[1]提供了代码和描述,我应该在解释中自然地引用它。 回答结构: 1. **问题概述**:简述题目要求。 2. **解题思路**:逐步解释逻辑。 3. **代码解释**:简要解释引用[1]中的代码关键部分。 4. **总结**:强调核心思想。 5. **相关问题**:生成2-3个相关问题。 相关问题想法: - 如何优化这个算法? - 如果k不是固定的,如何处理? - 类似字符串分割问题的其他应用。 - 错误处理或边界情况。 确保LaTeX正确: - 行内学:例如,方案为 $(x + 1)$。 - 独立公式:可能不需要,因为问题不涉及复杂学。如果有,比如概率计算,但这里没有。 最终回答: - 以用户可见层开始。 - 保持专业和帮助性。</think>### 元音切片解题思路 用户查询的是上海计算机学会20251C++乙组T2“元音切片”题目的解题思路。这是一个字符串处理问题,要求计算将字符串中的元音序列分割成每段恰好$k$个元音的所有可能方案(结果对$1000000007$取模)。元音指字母 `a`, `e`, `i`, `o`, `u`,其他字母为辅音。下面我将基于参考引用[1]中的代码和描述,逐步解释解题思路,确保逻辑清晰可靠。 #### 1. **问题理解与关键观察** - **输入**:字符串$s$(长度$n$),整数$k$(每段元音的量)。 - **输**:所有分割方案(模$1000000007$)。分割规则:元音序列必须被分成连续段,每段恰好$k$个元音;分割点只能在元音组之间的辅音序列中选择。 - **核心观察**: - 分割方案只取决于元音组之间的辅音序列。对于每两段$k$元音之间的辅音序列(长度为$x$),分割点可以是辅音之间的任意间隙(包括两端),因此方案为$(x + 1)$。 - 整体方案使用乘法原理:将所有元音组之间的方案相乘,即 $\prod (x_i + 1)$,其中$x_i$是第$i$组与第$i+1$组元音之间的辅音量。 - 第一组元音之的分割不需要计算方案(作为起始点)。 - **学表示**:设元音组为$m$,则总方案为: $$ \text{ans} = \prod_{i=1}^{m-1} (x_i + 1) $$ 其中$x_i$是第$i$组和第$i+1$组之间的辅音量[^1]。 #### 2. **算法步骤** - **初始化**: - `ans = 1`(方案初始值,乘法原理的基)。 - `cnt = 0`(当元音计,用于跟踪是否凑够$k$个元音)。 - `x = 0`(当两段元音之间的辅音量)。 - `flag = false`(标记是否遇到第一组元音;初始未开始)。 - **遍历字符串**(每个字符): - **如果是元音**: - `cnt++`(元音计加一)。 - 如果 `cnt == 1`(表示新元音组的开始): - 如果 `flag == false`,则设置 `flag = true`(标记第一组开始)。 - 否则(非第一组),将方案更新:`ans = ans * (x + 1) % 1000000007`(乘法原理,乘上当辅音序列的方案)。 - 重置 `x = 0`(辅音计归零,准备下一段)。 - 如果 `cnt == k`,则重置 `cnt = 0`(完成一组$k$元音)。 - **如果是辅音**: - 如果 `cnt == 0`(表示当处于两段元音之间),则 `x++`(辅音计加一)。 - **输结果**:遍历结束后,`ans` 即为总方案。 #### 3. **代码解释(基于引用[1])** 引用[1]中的C++代码实现了上述逻辑。以下是关键部分解析: ```cpp #include<bits/stdc++.h> using namespace std; int main() { cin >> t; // 测试用例 while(t--) { cin >> n >> k >> s; // 输入字符串长度n, 分段大小k, 字符串s long long ans = 1; int cnt = 0, x = 0; bool flag = 0; for(int i = 0; i < n; i++) { if (s[i] == &#39;a&#39; || s[i] == &#39;e&#39; || s[i] == &#39;i&#39; || s[i] == &#39;o&#39; || s[i] == &#39;u&#39;) { // 元音判断 cnt++; if (cnt == 1) { if (flag == 0) flag = 1; // 第一组开始 else { ans = ans * (x + 1) % 1000000007; // 乘法原理更新方案 } x = 0; // 重置辅音计 } cnt %= k; // 当cnt==k时重置为0 } else { // 辅音 if (cnt == 0) x++; // 只计两段元音之间的辅音 } } cout << ans << endl; } return 0; } ``` - **时间复杂度**:$O(n)$,一次遍历字符串。 - **空间复杂度**:$O(1)$,仅用常额外变量。 - **边界处理**: - 如果字符串中元音总不足$k$个,则无法分割,方案为0(但代码中`ans`初始为1,需确保输入合法)。 - 第一组元音忽略方案计算,避免多余乘法。 - 取模操作确保结果不溢[^1]。 #### 4. **核心思想总结** - **乘法原理**:分割方案由独立的选择点决定,每个元音组间的方案$(x_i + 1)$ 相乘。 - **贪心遍历**:通过计元音和辅音,动态识别分割点,无需额外存储。 - **实际应用**:类似问题可用于文本处理或据压缩,如固定长度模式的分割。 如果您有具体输入案例需要测试或优化建议,我可以进一步解释! ### 相关问题 1. 如果$k$值不固定(如每段元音可变),如何修改算法2. 如何处理字符串中包含大写字母或特殊字符的情况? 3. 这个算法在哪些实际场景中(如自然语言处理)有应用?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长春高老师信奥工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值