题目描述
在Pty学校附近,有一座名之为岳之麓的高山。Pty很喜欢和(哔——)一起爬山。
山的平面模型如下:
山由一个顶点集:A1,A2…An给定,保证Ai的x单调递增。我们将Ai和Ai+1之间连上线段,表示山的某一段。如下图所示:
Pty想要爬到这座山的最高的顶点,当两个顶点的高度相同时,我们认为x比较大的顶点要高一些。Pty不是盲人,所以他将会在爬山时采取一些策略,使得他能够尽量快的到达最高的顶点。
Pty从初始的顶点出发,往左右看去,他将朝他能够看到的最高的顶点方向走去。当走到每一个顶点时,他都会重新观察,如果这时看到的顶点比之前看到的顶点还要高,那么他将选择此时看到的顶点走去,直到他到达最高点为止。
例如上图中:Pty从A4点出发。他能够看到的最高点是A6,所以他将会向右侧走去。当他到达A5号点时,能够看到A1点比A6点更高,所以他会调转方向,向左侧走去。由于A1是最高的顶点,所以他将一直往左侧走,直到到达A1为止。
Pty想知道从每一个顶点出发,分别需要走过多少段才能到达最高点。例如上图中从A4出发需要走过5段才能到达最高点。
单调栈或一种机智的方法(我也不知道有没有名字)
如何处理每个点看到的最高点呢?
我们分两个方向考虑。
(我用了机智的方法,单调栈其实也类似)
设fa1[i]表示i在i的左侧能看到的最高点
设j=i-1,若i能看到fa1[j]则fa1[i],j都更新为fa1[j],否则i++
我们需要证明的是若i看不到fa1[j],那么fa1[j]~j的点i都看不到,然而这显然(不清楚就画个图看看)
所以可以O(n)处理出fa1,设fa2[i]表示i在i的右侧能看到的最高点,同样类似的处理。
设f[i]表示i能看到的最高点的高度,可以通过fa1[i],fa2[i]比较得出,
设p[i]看到的最高点的方向(1表示在i左侧,2表示在右侧)。
现在我们已经得出了每个点应该前进的方向,怎么求出从每一个点出发所需要经过的段数?假设我们现在从A号点出发,当它走到B号点时,看到了一座比A号点看到的更高的顶点,此时是不是就是相当于从B号点出发了呢?答案是肯定的。
所以我们让A指向B,可以发现最后的结构图将是一棵树,dfs一遍就能得出ans。
如何快速的找到B?
设f1[i]表示i在i的左侧看到的离i最近的满足f值比i大的点。
设j=i-1,若f[i]>f[j]则j=1[j],否则f1[i]=j
正确性同样显然。
设f2[i]表示i在i的右侧看到的离i最近的满足f值比i大的点。
类似做法可得f2。
最后根据p[i],f1[i],f2[i]来连边。dfs得出ans。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=200000+5;
int f1[maxn],f2[maxn],x[maxn],y[maxn],fa1[maxn],fa2[maxn],n,k[maxn],next[maxn],g[maxn],p[maxn],root,num;
ll ans[maxn],yy[maxn],f[maxn];
void add(int x,int y){
next[++num]=k[x];
k[x]=num;g[num]=y;
}
void read(int &n){
char ch=getchar();
while(ch!='-'&&(ch<'0')||(ch>'9'))ch=getchar();n=0;
int q=1;if (ch=='-') ch=getchar(),q=-1;
while(ch>='0'&&ch<='9')n=n*10+ch-'0',ch=getchar();n=n*q;
}
void dfs(int x){
int i=k[x];
while (i>0){
ans[g[i]]=ans[x]+abs(x-g[i]);dfs(g[i]);
i=next[i];
}
}
int main(){
read(n);
for (int i=1;i<=n;i++) read(x[i]),read(y[i]),yy[i]=(ll)y[i]*1000000+i;y[0]=y[n+1]=0;
for (int i=1;i<=n;i++){
int k=i-1,j=fa1[k];fa1[i]=k;bool bz=1;
ll k1=(y[k]-y[i]),k2=x[i]-x[k];
while (j>0&&bz) {
ll k3=y[j]-y[i],k4=x[i]-x[j];
if (k1*k4<=k3*k2) fa1[i]=j,j=fa1[j],k1=k3,k2=k4;else bz=0;
}if (y[fa1[i]]<y[i]) fa1[i]=0;
}fa2[n+1]=n+1;
for (int i=n;i>0;i--){
int k=i+1,j=fa2[k];fa2[i]=k;bool bz=1;
ll k1=(y[k]-y[i]),k2=x[k]-x[i];
while (j<=n&&bz){
ll k3=y[j]-y[i],k4=x[j]-x[i];
if (k1*k4<=k3*k2) fa2[i]=j,j=fa2[j],k1=k3,k2=k4;else bz=0;
}if (y[fa2[i]]<y[i]) fa2[i]=n+1;
}
for (int i=1;i<=n;i++){
int a=fa1[i],b=fa2[i];ll h=yy[i];
if (yy[a]>h) h=yy[a],p[i]=1;
if (yy[b]>h) h=yy[b],p[i]=2;f[i]=h;
}
for (int i=1;i<=n;i++){
int j=i-1;
while (j>0&&f[j]<=f[i]) j=f1[j];f1[i]=j;
if (f1[i]==0) f1[i]=fa1[i];
}
for (int i=n;i>0;i--){
int j=i+1;
while (j<=n&&f[j]<=f[i]) j=f2[j];f2[i]=j;
if (f2[i]==n+1) f2[i]=fa2[i];
}
for (int i=1;i<=n;i++){
if (p[i]==0) root=i;else
if (p[i]==1) add(f1[i],i);else add(f2[i],i);
}dfs(root);
for (int i=1;i<=n;i++) printf("%lld\n",ans[i]);
}