BZOJ3755 : Pty爬山

时间:2022-09-14 17:34:50

l[i],r[i]表示站在i点往左往右走能看到的最高峰,用栈维护凸壳求出

h[i]表示i点能看到的最高峰的高度

a[i],b[i]表示i点往左往右走时反悔的点,即第一个h[j]>h[i]的j,用单调栈求出

然后建树DFS一遍求出答案

#include<cstdio>
#define N 200010
typedef long long ll;
int n,i,x[N],y[N],l[N],r[N],q[N],a[N],b[N],t,g[N],nxt[N],v[N],ed,d[N],f[N];ll Y[N],h[N];
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline int abs(int x){return x>0?x:-x;}
void dfs(int x){for(int i=g[x];i;i=nxt[i])f[v[i]]=f[x]+abs(x-v[i]),dfs(v[i]);}
int main(){
read(n);
for(y[0]=y[n+1]=-1,i=1;i<=n;i++)read(x[i]),read(y[i]),Y[i]=1000000LL*y[i]+i;
for(q[t=0]=n,i=n-1;i;q[++t]=i--){
while(t&&(ll)(y[q[t]]-y[i])*(x[q[t-1]]-x[q[t]])<=(ll)(y[q[t-1]]-y[q[t]])*(x[q[t]]-x[i]))t--;
r[i]=q[t];
}
for(q[t=0]=1,i=2;i<=n;q[++t]=i++){
while(t&&(ll)(y[q[t]]-y[i])*(x[q[t-1]]-x[q[t]])>=(ll)(y[q[t-1]]-y[q[t]])*(x[q[t]]-x[i]))t--;
l[i]=q[t];
}
for(i=1;i<=n;i++){
if(Y[i]>Y[l[i]])l[i]=0;
if(Y[i]>Y[r[i]])r[i]=n+1;
h[i]=Y[i];
if(Y[l[i]]>h[i])h[i]=Y[l[i]],d[i]=1;
if(Y[r[i]]>h[i])h[i]=Y[r[i]],d[i]=2;
}
h[0]=h[n+1]=1LL<<60;
for(q[t=0]=0,i=1;i<=n;q[++t]=i++){
while(h[i]>h[q[t]])t--;
a[i]=q[t]>l[i]?q[t]:l[i];
}
for(q[t=0]=n+1,i=n;i;q[++t]=i--){
while(h[i]>h[q[t]])t--;
b[i]=q[t]<r[i]?q[t]:r[i];
}
for(i=1;i<=n;i++){
if(!d[i])t=i;
if(d[i]==1)add(a[i],i);
if(d[i]==2)add(b[i],i);
}
for(dfs(t),i=1;i<=n;i++)printf("%d\n",f[i]);
return 0;
}