1. 程式人生 > >洛谷P1823 [COI2007] Patrik 音樂會的等待(單調棧+二分查詢)

洛谷P1823 [COI2007] Patrik 音樂會的等待(單調棧+二分查詢)

洛谷P1823 [COI2007] Patrik 音樂會的等待(單調棧+二分查詢)

標籤:題解
閱讀體驗:https://zybuluo.com/Junlier/note/1333275

這個題不是很難,但是沒有轉過來還是難想的
可以先去做一下這個題:洛谷P1901 發射站
蒟蒻發現很多題解都是錯的呀,複雜度比較玄學吧
介紹一種標準的\(O(nlogn)\)的方法

單調棧

我們對於一個人作為方案中右邊那個人時我們算答案(為了不算重)
有哪些人我們看不到呢,無非是被它右邊的人擋住了是吧
那麼從左往右維護一個單調遞減的單調棧,單調棧中的人不會出現被擋住的情況(只有\(i\)看不到的情況後面會講)
自己想一下這裡很簡單

二分查詢

考慮肯定只有單調棧中的人會被\(i\)算入答案是吧
並且很容易發現一定是個連續的區間\([x,i-1]\)這不廢話嗎
那麼我們在單調棧中二分這個區間的左端點,顯然左端點就是\(i\)左邊第一個比\(i\)高的數
這不就是上面那個發射站的題目了嗎
計入答案的就是區間長度啦

程式碼極其簡單。。。

#include<bits/stdc++.h>
#define il inline
#define rg register
#define ldb double
#define lst long long
#define rgt register int
#define N 500050
using namespace std;
const int Inf=1e9;
il int read()
{
    int s=0,m=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')m=1;ch=getchar();}
    while( isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return m?-s:s;
}

int n,top;lst Ans;
int H[N],stk[N];

il void Calc(rgt x)
{
    rgt le=0,ri=top,mid,ret=0;
    while(le<=ri)
    {
        mid=(le+ri)>>1;
        if(H[stk[mid]]>x)ret=mid,le=mid+1;
        else ri=mid-1;
    }
    if(!ret)Ans+=top;
    else Ans+=top-ret+1;
}
int main()
{
    n=read();
    for(rgt i=1;i<=n;++i)H[i]=read();
    for(rgt i=1;i<=n;++i)
    {
        Calc(H[i]);
        while(top>0&&H[i]>H[stk[top]])--top;
        stk[++top]=i;
    }return printf("%lld\n",Ans),0;
}