[COI2007] Patrik

本文介绍了一种解决音乐会排队中人们互相可见问题的算法。通过使用单调栈,该算法可以确定有多少对人能够互相看见。文章详细解释了算法的工作原理,并提供了完整的代码实现。

N people are waiting in line to enter a concert. People get bored waiting so they turn and look for someone familiar in the line.
n个人排队等待进入音乐会。人们无聊地看看队伍里有没有自己熟悉的人。
Two persons A and B standing in line can see each other if they’re standing right next to each other or if no person between them is strictly taller than person A or person B.
A和B两个人如果能够互相看见,他们要么挨着,要么他们两人之间没有人严格高于A或B。
Write a program that determines the number of pairs of people that can see each other.
设计一个程序,求出人群中有多少对人能够互相看见。

随机的题。。题目第一眼看起来就是个单调栈。。
考虑 AA 怎么看到左边的人:
1.A1.A 一定能互相看到左边第一个比他高的人。
2.A2.A 可以看到左边单调递减区间的人。

这个东西维护一个单调递减的单调栈就好了。
然后对于相同高度的,我们可以缩点分类讨论一下记录块中的 sizesize
只有 AA 这个块中最左边的人能向左一览无余单调递减区间。而遇到比他高的人 A 整块都会看到。
如果碰到高度相同的人,显然两两都能看到,然后最后合并 sizesize

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<ll,ll>par;
typedef pair<par,ll>pii;
const ll INF=2e18;
const int MAXN=8e5+5;
#define mp make_pair

par a[MAXN];
pii sta[MAXN];
ll cnt=0,n,top=0,ans=0,recalc=0;

int main(){
    scanf("%lld",&n);
    recalc=n-1;
    for(int i=1;i<=n;i++){
        ll tmp;
        scanf("%lld",&tmp);
        if(tmp!=a[cnt].first)a[++cnt]=mp(tmp,1);
        else a[cnt].second++;
    }
    for(int i=1;i<=cnt;i++)if(a[i].second>2)recalc+=(a[i].second-1)*(a[i].second-2)/2;
//  a[0]=mp(INF,1);
//  sta[++top]=mp(a[0],0);
    for(int i=1;i<=cnt;i++){
        //cout<<ans<<endl;
        int flag=0;
        while(top&&sta[top].first.first<=a[i].first){
            if(sta[top].first.first==a[i].first){
                ans+=sta[top].first.second*a[i].second;
                flag=sta[top].first.second;top--;
                break;  
            }
            ans+=sta[top].first.second;
            if(i>1&&i-1==sta[top].second)ans--;
            top--;  
        }
        if(top)ans+=a[i].second;
        if(top&&sta[top].second==i-1)ans--;
        if(flag)a[i].second+=flag;
        sta[++top]=mp(a[i],i);
    }
    printf("%lld\n",ans+recalc);
    return 0;
}
/*
6
3
2
2
1
2
2
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值