---
# System prepended metadata

title: Codeforce 319 B. Psychos in a Line 解析(思維、單調棧)

---

---
title: Codeforce 319 B. Psychos in a Line 解析(思維、單調棧)
description: "Codeforce 319 B. Psychos in a Line 解析(思維、單調棧)"
disqus: hackmd
---
<meta name="robots" content="Codeforce 319 B. Psychos in a Line 解析(思維、單調棧)">
<!-- toc -->

# Codeforce 319 B. Psychos in a Line 解析(思維、單調棧)
今天我們來看看CF319B
[題目連結](https://codeforces.com/problemset/problem/319/B)

> **題目**
給一個數列，如果相鄰兩數，左邊大於右邊，那麼就可以殺死右邊的數字(被殺死的數字在當前一輪也可以殺右邊的數字)
求幾輪以後就沒有數字會自相殘殺了?

### 前言
第一眼看到就感覺會和單調棧有關，但是實在經驗不足，最後思緒被拉跑到找遞增和遞減數列了...

### 想法
我們都知道，單調棧最一開始就是拿來找一個元素往左或往右的，第一個比元素本身大或小的元素。
但這題還需要我們利用單調棧運行時的過程。
首先我們從數列左邊開始處理(跑單調棧找往左看第一個比自身大的)，並且將第$i$個元素在第幾輪會被殺掉儲存為$pre[i]$，接著注意到這件事(令第$i$元素往左看第一個大於的元素的index為$j$):$pre[i]=\min\{i-j,[j+1,i]區段中最大的pre[.]+1\}$，其中$i-j$代表最慢的情況就是由左邊第一大的元素慢慢殺過來，而後者代表$[j+1,i]$這一段都被殺完以後，下一個就是自己被殺。
而重點就在於尋找$[j+1,i]區段中最大的pre[.]$可以在stack pop的同時計算。其原因是因為:假設目前$[j,i]$區段已經有一些元素被pop走了，剩下等著還沒pop的元素當然有可能是最大值，要計入考慮，而這些剩下的元素都已經計算過$pre[i']=\min\{i'-j',[j'+1,i']區段中最大的pre[.]+1\}$，又，$i'-j'\ge[j'+1,i']區段中最大的pre[.]+1$。所以只要計算還在stack中的最大值，就等於整個$[j+1,i]$區間的最大值了。
最後只要輸出$pre[.]$中的最大值就好。

### 程式碼:
```cpp=
const int _n=1e5+10;
int t,n,a[_n];
int pre[_n];
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  cin>>n;rep(i,0,n)cin>>a[i];
  stack<int> s;rep(i,0,n){
    while(!s.empty() and a[s.top()]<a[i]){
      pre[i]=max(pre[i],pre[s.top()]);s.pop();
    }
    pre[i]=min(s.empty()?0:i-s.top(),pre[i]+1);
    s.push(i);
  }int maxx=-1e9;rep(i,0,n)maxx=max(maxx,pre[i]);
  cout<<maxx<<'\n';
  return 0;
}
```
標頭、模板請點Submission看
[Submission](https://codeforces.com/contest/319/submission/90472213)