专栏文章
题解:P12501 「ROI 2025 Day1」奥林匹克楼梯
P12501题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @minqg1mb
- 此快照首次捕获于
- 2025/12/02 06:40 3 个月前
- 此快照最后确认于
- 2025/12/02 06:40 3 个月前
前言:感觉大佬们只讲了怎么做,没写为什么要这么做 ,不适合我这种蒟蒻理解。所以我写了篇适合蒟蒻食用的题解。
题意
楼梯:左侧齐平,右侧不能往左陷
思路
根据题意,我们先记 ,代表 右侧有几个 。
然后,把模板横过来,发现题目就是让我们找最长不下降子串的每一项之和(不过有很多行)。
大体思路有了,现在要实现找每行(横着的)最长不下降子串。
首先,我们从右往左一列一列看(横着的从上到下),记录这格的 能从下往上延续多少格,记延续的格数 (由于每行是单独的,行内计算可以省略 ),那么这个 能做出 格贡献(请搭配题图理解)。
然后,我们从上往下(横着的从左到右)遍历,记以当前格为左下角(正看)的楼梯的面积为 ,。也就是当前 做的贡献加上前面它结束贡献的地方的贡献。以样例为例, 时,两格的楼梯持续了两个长度,那么 。
不过直接模拟时间复杂度是 ,会 TLE,我们要优化一下。
我们寻找最长不下降子串的特点是:从右到左,找第一个小于 的地方。所以我们能想到用单调栈来优化。单调栈能把时间优化成 。
我们的思路到此结束,接下来就要开始写代码啦~
代码
CPP#include <bits/stdc++.h>
using namespace std;
vector<vector<bool>> a;
vector<vector<int>> h; // 以(i,j)为准,右边有多少个1
vector<int> s,l;
// s:对于每个j,以i为左下角的楼梯的最大格数
// l:对于每个j,h[i][j]能向上持续多远(楼梯定义)
int mx;
int main(int argc, char **argv){
int n,m;
cin >> n >> m;
a = vector<vector<bool>>(n + 1,vector<bool>(m + 1));
h = vector<vector<int>>(n + 1,vector<int>(m + 1));
s = l = vector<int>(n + 1);
for (int i = 1;i <= n;i++){
string s;
cin >> s;
for (int j = 1;j <= m;j++){
a[i][j] = s[j - 1] - '0';
}
}
for (int i = 1;i <= n;i++){
h[i][m] = a[i][m];
for (int j = m - 1;j > 0;j--){
if (a[i][j]) h[i][j] = h[i][j + 1] + 1;
}
}
for (int j = m;j > 0;j--){
// for (int i = n;i > 0;i--){
// int k = i - 1;
// while (k >= 0 && h[k][j] >= h[i][j]) k--;
// l[i] = i - k;
// }
// for (int i = 1;i <= n;i++){
// s[i] = s[i - l[i]] + l[i] * h[i][j];
// mx = max(mx,s[i]);
// }
stack<int> st;
for (int i = n;i > 0;i--){
l[i] = i; // 默认能持续到最顶上
while (!st.empty() && h[st.top()][j] > h[i][j]){
l[st.top()] = st.top() - i;
st.pop();
}
st.push(i);
}
for (int i = 1;i <= n;i++){
s[i] = s[i - l[i]] + l[i] * h[i][j];
mx = max(mx,s[i]);
}
}
cout << mx;
return 0;
}
upd 2025.10.7:纠正一处笔误。
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...