诺诺的队列

诺诺的队列

题目描述

诺诺表现、成绩双优,于是校长给她一笔money,让她去外地玩玩。由于本地没有飞机场,所以诺诺只能坐火车去咯。所以诺诺今天去火车站买票,却看到了N多人在火车站里啊,诺诺一阵头晕。机灵的她突然发现,有N个人在队伍里(和上文的N毫无关系- -||),人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的。
诺诺想计算出有多少对人可以互相看见,那么你能帮帮诺诺吗?

本题数据范围:
40%的测试数据:N≤10000;
80%的测试数据:N≤100000;
100%的测试数据:N≤500000。

输入格式 2116.in

输入的第一行包含一个整数N (1≤N≤500 000),表示队伍中共有N个人;
接下来的N行中,每行包含一个整数,表示人的高度,人的高度<1000000。

输出格式 2116.out

输出仅有一行,包含一个数S,表示队伍中共有S对人可以互相看见。

输入样例 2116.in

7
2
4
1
2
2
5
1

输出样例 2116.out

10

         看到题目,我们可以先从暴力方法入手。我们需要枚举i,j两个人(j<i),对每个人往前看能够看到的人进行计数即可。

    很显然,这样的时间复杂度是O(N^2),期望得分只有40。因此,需要一个更加高效的办法。

    通过分析,可以发现,本题是具有某些特殊性质的。

    如果i前面的y>x且a[y]>a[x],那么i一定看不到x。从中可以发现单调性——i所能看到的人,从左往右看,它们的身高一定是不上升的序列。

    如果a[j]>a[i],那么j就挡住了i的视线,i一定看不到j左边的人。因此,i最远只能看到第一个高于它的人(从右往左数)。

    由于此处没有窗口长度的限制,因此此处是维护一个单调栈,栈中元素是单调递减的。

    因此,将a[i]入栈时,要将栈中小于a[i]的元素出栈,并且,这些出栈元素是i能够看到的人,将其计入答案中。而且,第i个人顶多只能看到一个比它高的人,因此如果栈中存在比a[i]大的元素,计数加1。

    但是,我们还需要考虑,同一身高,可能有多个人,因此需要记录元素的实际数量,因此计数时应为ans+=st[top--].num,而不是ans++。

    而且,第i个人是可以看到栈中与它一样高的人的。因此需要特殊处理:如果栈顶元素等于a[i],则要另加,ans+=st[top].num,且不新建元素,st[top].num++即可;否则将a[i]入栈,st[++top].num=1。

         代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=500005;
int n;
long long a[MAXN];
long long q[MAXN],num[MAXN];
long long ans,len,head,tail;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)    cin>>a[i];
    head=1,tail=1;
    q[1]=a[1];
    num[1]=1;
    for(int i=2;i<=n;i++)
    {
       while(head<=tail&&q[tail]<a[i])
       {
           ans+=num[tail];
           tail--;
       }
       if(q[tail]==a[i])
       {
           ans+=num[tail];
           num[tail]++;
           if(tail>1) ans++;
       }
       else
       {
           if(tail>0) ans++;
           q[++tail]=a[i];
           num[tail]=1;
       }
    }
    cout<<ans<<endl;
    return 0;
}

 

         值得一提的是,这道题我改了很久才通过,一开始我的主程序是这样的:

    for(int i=2;i<=n;i++)

    {

       while(head<=tail&&q[tail]<=a[i])

       {

           ans+=num[tail];

           tail--;

       }

       if(tail>0) ans++;

       if(q[tail+1]==a[i])//有相同的,被删了。

       {

           tail++;

           num[tail]++;

       }

       else

       {

           q[++tail]=a[i];

           num[tail]=1;

       }     

     (写法与标程有点不同,现将小于等于的都出栈,再将相等的入栈)

    这个程序只有50分。为什么?正是棕色的这句话出现了问题。q[tail+1]可能并不是这次出栈的可能解,可能是之前就出栈的无用解,如果它正好等于a[i],它反倒成了可能解,就会造成出错。

    这个问题也很容易解决,只要将无用解的信息完全清空即可。(就是在while(head<=tail&&q[tail]<=a[i])循环中加了一句if(q[tail]<a[i])   q[tail]=0就AC了)。感谢lkb同学的热心帮助!

    它给我敲了一个大大的警钟——无用的信息,可能会造成困扰,一定要清空,才能避免错误!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值