t老师的做法好神……
题目描述
桌面上有 n 个水果,分别是苹果和橘子。Bytea需要从水果中选择连续的一个区间,并从左到右或从右到左拿水果,且过程中橘子的数量必须始终不小于苹果的数量。求最长的区间大小。
输入格式
第一行一个整数 n($1 \le n \le 100000$),表示水果个数。 接下来一行共有 n 个字符$a_1, a_2, ..., a_n (a_i \in \{j,p\})$,分别表示苹果和橘子(波兰语)。
输出格式
输出一行共一个数字,表示最长的区间大小。
样例输入
6
jpjppj
样例输出
4
数据范围与提示
对于 $20\%$ 的数据,$n \le 1000$.
对于 $50\%$ 的数据,$n \le 10000$.
对于所有数据,$1 \le n \le 100000$.
题目分析
做法来源:「LOJ #2430」「POI2014」沙拉餐厅 Salad Baralad Bar
令$d_i$为$\sum_{x=1}^{i}\{[s_x=='p']-[s_x=='j']\}$,那么合法区间的充要条件就是$i \in (l,r), d_l \le d_i \le d_r$。(这个转化超级妙的)
那么用单调栈找到离以$i$为右端点区间的最近$l$,满足$d_l>d_i$,于是左端点就是$[l+1,r]$间$d_i$最小值的位置。
所以在单调栈过程中顺带维护以$i$为起点区间最小值的位置。有个小细节就是利用$f[0]=0$这么一个初值,来做相当于哨兵节点一样的作用,避免了$d_i<0$被判断合法。
感觉我单调栈并不熟练……
1 #include<bits/stdc++.h> 2 const int maxn = 1000035; 3 4 int n,ans,cnt,top,stk[maxn],d[maxn],f[maxn]; 5 char s[maxn]; 6 7 int main() 8 { 9 scanf("%d%s",&n,s+1); 10 for (int i=1; i<=n; i++) 11 { 12 d[i] = d[i-1]+(s[i]=='p'?1:-1); 13 while (top&&stk[top] <= d[i]) 14 { 15 if (d[f[top-1]] > d[f[top]]) f[top-1] = f[top]; 16 top--; 17 } 18 if (d[f[top]] <= d[i]) ans = std::max(ans, i-f[top]); 19 stk[++top] = d[i], f[top] = i; 20 } 21 printf("%d\n",ans); 22 return 0; 23 }
END