Description
Input
输入文件的第一行含有一个正整数 n,代表山的顶点数。
接下来 n 行,每行包含两个整数 x i 和 y i ,代表一个顶点的坐标。输入保证 x i单调递增。
Output
输出 n 行,第 i 行包含一个整数,代表从第 i 个顶点出发走到最高点需要经过多少段。
Sample Input
5
1 5
2 4
3 9
4 0
5 2
Sample Output
2
1
0
1
2
Data Constraint
Hint
Solution
我们设 l[i] (r[i]) 表示点 i 向左(右)看能看到的点最高是哪一个。
这用单调栈可以
O(N) 求出来——左右扫一遍就可以了。之后我们发现最难统计的就是一个点不断发现新的高点,从而一直拐弯。
那么我们又设 fl[i] (fr[i]) 表示点 i 向左(右)第一个能看到更高点的位置。
这我们也可以近乎
O(N) 求出来,一样是扫一遍,用前面的 fl[] 更新当前的 fl[i] 。那么点 i 能看到的最高高度就是
f[i]=max(fl[i],fr[i]) ,走的方向也就确定了。之后我们以最高点为 root ,连边 (fl[i],i) (或 fr[i] ,以走的方向而定)。
显然这是一个树结构,因为每个点始终会到最高点(一定连通),目标点又越来越高(没环)。
于是跑一边 dfs ,就能算出每个点走的段数了。
时间复杂度近乎 O(N) 。
Code
#include<cstdio>
#include<cctype>
using namespace std;
const int N=2e5+5;
struct data
{
int x;
long long y;
}a[N];
int top,tot,root;
int first[N],nex[N],en[N];
int l[N],r[N],st[N],ans[N],fl[N],fr[N],fx[N];
long long f[N];
double d[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<1)+(X<<3)+(ch^48),ch=getchar();
return w?-X:X;
}
inline void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline double get(int x,int y)
{
return (a[x].y-a[y].y)*1.0/(a[x].x-a[y].x);
}
inline int abs(int x)
{
return x<0?-x:x;
}
void dfs(int x)
{
for(int i=first[x];i;i=nex[i])
{
ans[en[i]]=ans[x]+abs(x-en[i]);
dfs(en[i]);
}
}
inline void insert(int x,int y)
{
nex[++tot]=first[x];
first[x]=tot;
en[tot]=y;
}
int main()
{
int n=read();
for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
for(int i=1;i<=n;i++)
{
while(top>1 && d[top]<=get(i,st[top])) top--;
if(a[st[top]].y>a[i].y) l[i]=st[top];
st[++top]=i;
if(top>1) d[top]=get(i,st[top-1]);
}
top=0;
for(int i=n;i;i--)
{
while(top>1 && d[top]>=get(i,st[top])) top--;
if(a[st[top]].y>=a[i].y) r[i]=st[top]; else r[i]=n+1;
st[++top]=i;
if(top>1) d[top]=get(i,st[top-1]);
}
r[n]=n+1;
for(int i=1;i<=n;i++) a[i].y=a[i].y*1000000+i;
for(int i=1;i<=n;i++)
{
long long h=a[i].y;
if(a[r[i]].y>h) h=a[r[i]].y,fx[i]=2;
if(a[l[i]].y>h) h=a[l[i]].y,fx[i]=1;
f[i]=h;
}
for(int i=1;i<=n;i++)
{
int j=i-1;
while(j && f[i]>=f[j]) j=fl[j];
fl[i]=j?j:l[i];
}
for(int i=n;i;i--)
{
int j=i+1;
while(j<=n && f[i]>=f[j]) j=fr[j];
fr[i]=j<=n?j:r[i];
}
for(int i=1;i<=n;i++)
if(!fx[i]) root=i; else
if(fx[i]==1) insert(fl[i],i); else insert(fr[i],i);
dfs(root);
for(int i=1;i<=n;i++) write(ans[i]),putchar('\n');
return 0;
}