题目:
给定一个序列a_1,a_2,…,a_n。其中a_1是最大的数,没有其他数与它相等
对于从第二个数开始的每个数a_i,请找到位置在a_i之前且比a_i大的。位置上距离a_i最近的的数a_j.i-j为a_i的前向距离
对于给定的序列,输出所有数的前向距离之和。
输入格式:
第一行包括一个整数n,表示序列长度
第二行包括n个正整数,为给定的序列
输出格式:
输出一个整数,表示序列中所有数的前向距离之和。
样例输入
8
9 1 3 5 2 7 6 3
输出
14
数据规模:
1<=n<=100000
解法
1.暴力
简单粗暴,依次查找来寻找比a[i]大的值,此处不再赘述。
2. 并查集
既然要寻找比a[i]大的值,那么让每一个被查询的值都比上一个被查询的值大,就可以节省很多时间了。恰好查询时前面的都被查询过,每个a[i]前面的 j 都已知,则可以使用每一个被查询数作为跳板,直接跳向它前面比它大的值。
具体操作使用并查集算法,以bf数组储存该数前面的大值的下标
代码:
#include<stdio.h>
typedef long long ll;
int main()
{
int i,j,n;
ll sum=0,a[1001],bf[1001];//bigger front
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%lld",&a[i]);
}
for(i=1;i<n;i++)
{
bf[i]=i-1;
while(a[i]>=a[bf[i]])//a[bf[i]]为查询的项前的大值,bf[i]为它的下标。
{
bf[i]=bf[bf[i]];//bf[i]的值为a[bf[i]]前的大值的下标
}
sum=sum+i-bf[i];
}
printf("%lld",sum);
return 0;
}
3. 单调栈
接下来就是本题常规做法单调栈啦。
单调栈特性
(1)栈中数据单调,可递增可递减
(2) 后进先出特性:越靠近栈底的元素越早进栈,可以意会为摞起来的一串数据。
(3)只可对栈顶的数操作
举例:
将{10,20,50,4,3,90}存入某单调递增栈中:
- 栈为空,存入10,此时栈中{10}
- 20>10,符合条件,存入20,此时{10,20}
- 50>20,符合条件,存入50,此时{10.20,50}
- 4<50,不断使栈顶元素出栈,直至符合条件,存入4,此时{4}
- 3<50,原理同上,{3}
- 90>3,符合条件,存入90,此时{3,90}
本题代码
#include<stack>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int main()
{
stack<int>s;
ll sum=0;
int n,i,a[100001],in[100001];//in[i]用于储存a[i]前面的大值的下标,作用类似于上面的bf
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
if(i==0) s.push(i);//栈为空,一定压入i值
else
{
while(a[i]>a[s.top()])
{
s.pop();
}
in[i]=s.top();
s.push(i);
if(a[i]==a[in[i]]) in[i]=in[in[i]];//两值相等情况须特殊考虑
sum+=(i-in[i]);
}
}
printf("%lld",sum);
return 0;
}
具体操作
- 最大值一定为a[0],故将0压入栈中
- 当栈顶值小于a[i]时,去除栈顶,直至新的栈顶大于a[i],再压入i,以保障栈中单调
- 这样,每当a[i]即将被压入栈时,栈顶的值即为a[i]前的大值的数组下标
- 用一个数组in存储每个a[i]前的大值的数组下标
- 排除特殊情况两值相等的干扰,(如{9,2,2,2,2},{9,2 ,1,2,1})以相同的数为跳板,找寻比它大的数,原理类似上文并查集做法
单调栈链接知识
基本用法
头文件 #include< stack >
库函数:
(1)empty
语法: bool empty();
如当前堆栈为空,empty() 函数 返回 true 否则返回false.
(2)pop
语法: void pop();
pop() 函数移除堆栈中最顶层元素。
(3)push
Syntax: void push( const TYPE &val );
push() 函数将 val 值压栈,使其成为栈顶的第一个元素。如:
stack<int> s;
for( int i=0; i < 10; i++ )
s.push(i);
(4)size
语法: size_type size();
size() 函数返当前堆栈中的元素数目。如:
stack<int> s;
for( int i=0; i < 10; i++ )
s.push(i);
cout << "This stack has a size of " << s.size() << endl;
(5)top
语法: TYPE &top();
top() 函数返回对栈顶元素的引用. 举例,如下代码显现和清空一个堆栈。
while( !s.empty() ) {
cout << s.top() << " ";
s.pop();
}
例题
有待补充