诺诺的队列
题目描述
诺诺表现、成绩双优,于是校长给她一笔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同学的热心帮助!
它给我敲了一个大大的警钟——无用的信息,可能会造成困扰,一定要清空,才能避免错误!