洛谷 P1440 求m区间内的最小值

本文介绍了一种使用单调队列算法解决数列中求m区间内最小值的方法。对于一个包含n项(n≤2000000)的数列,需要找出每个位置上,从前m个数中找到最小值。如果前面的数不足m项,则从第一个数开始,若无前导数则输出0。通过输入n和m,以及数列的各个元素,程序将输出每项前m个数的最小值。

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

题目描述

一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值。若前面的数不足m项则从第1个数开始,若前面没有数则输出0。

输入输出格式

输入格式:

第一行两个数n,m。

第二行,n个正整数,为所给定的数列。

输出格式:

n行,第i行的一个数ai,为所求序列中第i个数前m个数的最小值。

输入输出样例

输入样例#1:
6 2
7 8 1 4 3 2
输出样例#1:
0
7
7
1
1
3 

说明

【数据规模】

m≤n≤2000000



单调队列模板题。。。

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,h,t,a[2000005],q[2000005];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	printf("0\n");
	q[++h]=1;
	t=1;
	for(int i=2;i<=n;i++)
	{
		printf("%d\n",a[q[h]]);
		if(q[h]<=i-m)
			++h;
		while(a[i]<=a[q[t]]&&t>=h)
			--t;
		q[++t]=i;
	}
	return 0;
}

### P1816 题目解析 #### 问题描述 P1816《忠诚》的题目背景设定在一个庄园中,老管家为财主工作了十年。为了验证管家的忠诚度,财主设计了一种特殊的测试方法。具体来说,财主要管家记录每日账目,并对其进行查询操作。 输入据分为两部分: - **账目记录**:每一天有若干次账目记录,每条记录有一个具体的值。 - **查询请**:每次查询指定一段区间 `[a, b]`,询问该区间最小值。 目标是实现一个程序,在满足高效性和准确性的同时处理这些查询请[^4]。 --- #### 解决方案分析 此题的核心在于如何快速响应大量的区间最小值查询。以下是两种常见的解决思路: ##### 方法一:暴力枚举法 对于每一个查询 `(a, b)`,可以直接遍历组中的子区间 `[a, b]` 来找到其中的最小值。然而,这种方法的时间复杂度较高,达到 \(O(Q \times N)\),其中 \(Q\) 是查询次,\(N\) 是账目的总。当据规模较大时,这种算法可能无法通过所有测试用例。 ```cpp #include <bits/stdc++.h> using namespace std; int main() { int n, q; cin >> n >> q; // 账目量和查询次 vector<int> accounts(n); for (int i = 0; i < n; ++i) { cin >> accounts[i]; } while (q--) { int a, b; cin >> a >> b; // 查询范围 [a, b] int min_val = INT_MAX; for (int i = a - 1; i <= b - 1; ++i) { // 注意索引偏移 if (accounts[i] < min_val) { min_val = accounts[i]; } } cout << min_val << endl; } } ``` 虽然简单易懂,但在大规模据下效率低下[^4]。 --- ##### 方法二:预处理 + 倍增 RMQ(Range Minimum Query) 倍增 RMQ 是一种高效的解决方案,其核心思想是对原组进行预处理,从而加速后续的查询过程。具体步骤如下: 1. 定义 `f[i][j]` 表示从位置 `i` 开始,长度为 \(2^j\) 的子区间最小值。 2. 利用动态规划的思想计算所有的 `f[i][j]` 值: - 当 \(j = 0\) 时,`f[i][0] = nums[i]`; - 对于更大的 \(j\),可以由两个更小区间的结果合并得到: \[ f[i][j] = \min(f[i][j-1], f[i + 2^{j-1}][j-1]) \] 3. 在查询阶段,给定区间 `[a, b]`,可以通过分解成两个重叠的部分来快速得出答案。假设区间长度为 \(len = b - a + 1\),则令 \(k = \lfloor\log_2(len)\rfloor\),最终结果为: \[ \text{result} = \min(f[a][k], f[b - 2^k + 1][k]) \] 下面是完整的代码实现: ```cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 5e4 + 5; const int LOG = 17; // log2(50000) // 动态规划表 int f[MAX_N][LOG]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; // 组大小和查询次 vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; // 输入账目据 } // 初始化 dp 表 for (int i = 0; i < n; ++i) { f[i][0] = nums[i]; } // 计算更高层次的状态转移 for (int j = 1; j < LOG; ++j) { for (int i = 0; i + (1 << j) - 1 < n; ++i) { f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); } } // 处理查询 while (m--) { int l, r; cin >> l >> r; // 查询范围 [l, r] --l; --r; // 转换为零基索引 int k = floor(log2(r - l + 1)); cout << min(f[l][k], f[r - (1 << k) + 1][k]) << "\n"; } } ``` 这段代码利用倍增技术实现了高效的区间最值查询,时间复杂度降到了 \(O(N \log N + Q \log N)\)[^4]。 --- #### 总结 针对 P1816,《忠诚》这道题目提供了多种解法。如果追简洁性,可以选择暴力枚举;但如果希望优化性能,则推荐采用倍增 RMQ 技术。后者不仅能够显著提升运行速度,还具有较高的通用性,适用于其他类似的区间查询场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值