在O(logn)时间内找到数组中离每个数最近而又比它大的数的下标

时间:2022-09-30 19:24:06

题目描述

  Farmer Zhao's N cows (1 ≤ N ≤ 1,000,000) are lined up in a row. So each cow can see the nearest cow which is taller than it. You task is simple, given the height (0 < height ≤ 109) of each cow lined up in the row, to calculate the distance between each cow and its nearest taller cow, if it is the tallest cow in the row, such distance is regarded as n. You should output the average distance.

输入

  For each test case:
Line 1: One integers, N
Lines 2: N integers. The ith integer is the height of the ith cow in the row.

输出

  The average distance to their nearest taller cow, rounded up to 2 decimals.

示例输入

7
7 6 5 8 6 4 10

示例输出

2.43

y[p]指从p点往前找的第一个比它大的值的下标。

z[p]指从p点往后找的第一个比它大的值的下标。

分析一下为什么能省时间:假设当前点i,那么p从i-1开始往前找起,如果碰到x[p]比x[i]大的,那么就直接跳出循环给y[i]赋值;如果x[p]比x[i]小,提取y[p],那么易知从(y[p],p]这个区间内的所有值都比x[i]小(为什么?假如这个区间内有比x[i]大的值的话,y[p]'取区间内的下标比y[p]显然更优,这就产生矛盾)令p'=y[p],那就可以直接跳过这个区间从p'开始往前找了。

#include<bits/stdc++.h> 
#define ll long long
#define EPS 1e-8
using namespace std;
int x[1000005];
int y[1000005];
int z[1000005];
int main(){
int n;
while(~scanf("%d",&n)){
x[0]=2000000000;
x[n+1]=2000000000;
for(int i=1;i<=n;++i){
scanf("%d",&x[i]);
int p=i-1;
while(x[p]<=x[i]){ //如果比它小,那么把坐标p移动到更前的y[p]继续找,缩短时间
p=y[p];
}
y[i]=p;
}
for(int i=n;i>=1;--i){
int p=i+1;
while(x[p]<=x[i]){
p=z[p];
}
z[i]=p;
}
double s=0;
for(int i=1;i<=n;++i){
if(y[i]==0) y[i]=-2000000000;
if(z[i]==n+1) z[i]=2000000000;
s+=min(min(i-y[i],z[i]-i),n);
}
printf("%.2f\n",s/n+EPS); //这题必须加EPS才能过
}
return 0;
}