星星之火

[COI2007] [luogu P1823] Patrik 音乐会的等待 解题报告 (单调栈)

题目链接:https://www.luogu.org/problemnew/show/P1823

题目:

N个人正在排队进入一个音乐会。人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。

队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的。

写一个程序计算出有多少对人可以互相看见。

题解:

我们发现,其实就是算每个人向前看能看到多少个人之和对吧?

发现如果1号既比2号,又比2号靠后,后面的人就不可能看得到2号

于是我们考虑维护一个单调递减的栈,在不含重复大小元素的情况下,发现弹出的元素的数目就可以直接累加进ans里,特判一下如果这个时候还有元素在栈里那么显然也是看得到的,ans++

但是怎么处理重复的元素呢?我们考虑把重复的元素合并起来一起处理,具体实现就是开个pair,first记录高度,second记录人数

luogu上别忘了开long long

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
typedef long long ll;

using std::pair;
const int N=5e5+15;
int n,r;
ll ans;
ll a[N];
pair <int,int> q[N];
inline ll read()
{
    char ch=getchar();
    ll s=0,f=1;
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=n;i++)
    {
        pair <int,int> p(a[i],1);
        while (r&&q[r].first<=a[i]) 
        {
            ans+=q[r].second;
            if (q[r].first==a[i]) p.second+=q[r].second;
            r--;
        }
        if (r) ans++;
        q[++r]=p;
    }
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2018-09-20 14:51  星星之火OIer  阅读(201)  评论(0编辑  收藏  举报