A10 度假酒店 题解复盘
模块:前缀和思想 + 预处理
类型:区间排除查询 / 左右最大值维护
目标:每天有一个不可用区间 L,R,求剩余房间中人数最大的房间容量。
基本信息
| 项目 | 内容 |
|---|---|
| 题目编号、来源 | A10 - 度假酒店 |
| 训练层级 | A基础算法 / 区间查询 |
| 知识版块 | 前缀最大值、后缀最大值、预处理 |
| 核心模型 | 多次询问区间删除后的最大值查询 |
解题前・关键信号识别
| 维度 | 分析 |
|---|---|
| 目标、约束、底层结构 | 有 N 个房间,每天给出一个不可使用区间 L,R,需要找到剩余房间中的最大容量。核心是:查询一个数组去掉中间一段后的最大值。 |
| 数据规模 | N≤100000,D≤100000。如果每次查询都遍历剩余房间,复杂度 O(ND),会超时。需要预处理。 |
| 候选算法和依据 | ① 暴力枚举剩余房间:O(ND),不可行。 ② 预处理左侧最大值和右侧最大值:正确。因为删除 L,R 后,只可能来自两个区域:1~L-1 或 R+1~N。 |
| 复杂度预判 | 预处理:O(N) 每次询问:O(1) 总复杂度:O(N+D) |
解题后・外化复盘
| 维度 | 内容 |
|---|---|
| 实现结构 / 核心思路 | ① 提前计算每个位置左侧最大房间容量。 ② 提前计算每个位置右侧最大房间容量。 ③ 查询 L,R 被删除后,只需要比较: 左边:pre[L-1] 右边:suf[R+1] 答案: cpp<br>max(pre[L-1],suf[R+1])<br> |
| 错因回溯 | 这题容易想到每次删除区间后重新扫描剩余房间,但由于 D 很大,会导致超时。关键是发现查询结构固定,可以提前维护信息。 |
| 边界和易错点 | 1. 左边范围是 1~L-1,所以使用 pre[L-1]。 2. 右边范围是 R+1~N,所以使用 suf[R+1]。 3. 后缀数组需要开到 n+2,否则访问 suf[R+1] 时可能越界。 4. 当删除中间区间时,不需要考虑 L,R 内的数据。 |
| 下次看到什么信号,我应该想到这个方法 | 看到: ① 多次询问数组区间变化后的最大/最小值 ② 每次删除一个连续区间 ③ 查询剩余部分的信息 想到:预处理前缀 + 后缀信息。 |
AC完整代码
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n;
cin>>n;
int a[n+2];
int pre[n+2]={0};
int suf[n+2]={0};
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=max(pre[i-1],a[i]);
}
for(int i=n;i>=1;i--){
suf[i]=max(suf[i+1],a[i]);
}
int d;
cin>>d;
while(d--){
int l,r;
cin>>l>>r;
cout<<max(pre[l-1],suf[r+1])<<endl;
}
return 0;
}
CSP202412B 梦境巡查 题解复盘
模块:数学推导 + 前缀最大值 + 后缀最大值
类型:差分思想 / 区间最大值维护
目标:对于每个可能失效的补给点 i,求最少需要携带多少初始能量 wi,保证完整巡查。
基本信息
| 项目 | 内容 |
|---|---|
| 题目编号、来源 | CSP202412B 梦境巡查 |
| 训练层级 | CSP 第二题训练 |
| 知识版块 | 前缀和、后缀处理、数学建模 |
| 核心模型 | 将巡查过程转化为初始能量需要满足的一系列不等式,求最大缺口 |
解题前・关键信号识别
| 维度 | 分析 |
|---|---|
| 目标、约束、底层结构 | 巡查过程中,每经过一个区域都会消耗 a[i],并获得补给 b[i]。如果某个区域补给失效,需要增加初始能量。目标是求每个 i 失效时的最小初始能量。 |
| 数据规模 | n≤10⁵。如果每次模拟一次巡查,需要 O(n²),无法通过。需要预处理。 |
| 候选算法和依据 | ① 暴力模拟每个失效点:O(n²),只能过小数据。 ② 将能量变化转化为前缀差值,求过程中最大亏空。 ③ 利用前缀最大值和后缀最大值快速回答每个 i。 |
| 复杂度预判 | 预处理 O(n),每个答案 O(1),总复杂度 O(n)。 |
解题后・外化复盘
| 维度 | 内容 |
|---|---|
| 实现结构 / 核心思路 | ① 把初始能量看成需要抵消整个过程中的最大亏空。 ② 如果第 i 个补给失效,那么: 左边 1~i-1 按正常情况计算。 右边 i~n 少了 b[i] 补给。 ③ 所以答案: cpp<br>max(pre[i-1],suf[i]+b[i])<br> |
| 错因回溯 | 容易直接模拟每个失效位置: 枚举 i,然后重新走一遍 n 个区域。 复杂度 O(n²),n=10⁵ 时无法通过。直接模拟有80分 关键是发现每个位置的答案都来自固定的前缀和后缀信息。(受上面那个题影响想到前缀和后缀) |
| 边界和易错点 | 1. 区域编号从 1 开始,但是 a[0] 是出发消耗。 2. b[i] 只影响到 i 号区域之后。 3. 失效区域 i 的补给需要从后半部分答案中补回来,所以出现 +b[i]。 4. 使用 long long,避免累计能量超过 int。 |
| 下次看到什么信号,我应该想到这个方法 | 看到: ① 一个过程不断累积资源变化 ② 需要保证全过程不失败 ③ 每个位置询问一次最大缺口 想到:前缀累计量 + 最大亏空。 |
AC完整代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll a[100005];
ll b[100005];
ll pre[100005];
ll suf[100005];
ll d[100005];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=0;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
ll suma=0;
ll sumb=0;
for(int i=0;i<=n;i++){
suma+=a[i];
if(i>=1)
sumb+=b[i];
d[i]=suma-sumb;
}
pre[0]=d[0];
for(int i=1;i<=n;i++){
pre[i]=max(pre[i-1],d[i]);
}
suf[n]=d[n];
for(int i=n-1;i>=1;i--){
suf[i]=max(suf[i+1],d[i]);
}
for(int i=1;i<=n;i++){
ll ans=max(
pre[i-1],
suf[i]+b[i]
);
if(i>1)
cout<<" ";
cout<<ans;
}
return 0;
}