【YbtOJ#482】爬上山顶
题目
题目链接:https://www.ybtoj.com.cn/contest/116/problem/2
\(n\leq 5\times 10^5,x,y\leq 10^6\)。
思路
考虑求出点 \(i\) 左右分别能看到的最高点,然后在两者之间取最大值。
以左边为例,可以发现肯能造成贡献的一定位于上凸壳上,然后点 \(i\) 被贡献到的其实就是我们把点 \(i\) 插入到单调栈并维护好之后,单调栈中栈顶第二个元素。
求好了点 \(i\) 能看到的最高点 \(\text{nxt}_i\),接下来求点 \(i\) 到山顶的步数。
我们把点按照 \(\text{nxt}_i\) 的高度从大到小排序(高度相同右边在前),然后依次插入 set 中,每次插入前在 set 中找到这个点 \(x\) 前进方向上遇到的第一个点 \(y\),那么 \(x\) 在走到 \(y\) 之后就会向 \(\text{nxt}_y\) 走。那么显然 \(ans_x=ans_y+|x-y|\)。其实类似一个拓扑排序的过程。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int N=500010,Inf=1e9;
int n,rt,top,X[N],Y[N],st[N],nxt[N];
ll ans[N];
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void write(ll x)
{
if (x>9) write(x/10);
putchar(x%10+48);
}
struct node
{
int id,nxt;
}b[N];
bool cmp(node x,node y)
{
return (Y[x.nxt]==Y[y.nxt])?(x.nxt<y.nxt):(Y[x.nxt]>Y[y.nxt]);
}
double slope(int i,int j)
{
return 1.0*(Y[j]-Y[i])/(X[j]-X[i]);
}
void solve()
{
for (;top;top--) st[top]=0;
for (reg int i=1;i<=n;i++)
{
while (top>1 && slope(st[top],st[top-1])<slope(st[top],i))
st[top]=0,top--;
st[++top]=i;
int j=st[top-1];
if (Y[j]>=Y[nxt[i]]) nxt[i]=j;
}
}
void flip()
{
for (reg int i=1;i<=n/2;i++)
{
swap(X[i],X[n-i+1]); swap(Y[i],Y[n-i+1]);
swap(nxt[i],nxt[n-i+1]);
}
for (reg int i=1;i<=n;i++)
{
if (nxt[i]) nxt[i]=n-nxt[i]+1;
X[i]=n-X[i]+1;
}
rt=n-rt+1;
}
void dp()
{
for (reg int i=1;i<=n;i++)
b[i]=(node){i,nxt[i]};
sort(b+1,b+1+n,cmp);
set<int> s;
s.insert(0); s.insert(Inf);
for (reg int i=1;i<=n;i++)
{
int j=b[i].id,k=b[i].nxt;
if (j==rt) continue;
if (k<j)
{
int p=*(--s.lower_bound(j));
if (p<=k) ans[j]=ans[k]+j-k;
else ans[j]=ans[p]+j-p;
}
else
{
int p=*s.upper_bound(b[i].id);
if (p>=k) ans[j]=ans[k]+k-j;
else ans[j]=ans[p]+p-j;
}
s.insert(j);
}
}
int main()
{
freopen("mountain.in","r",stdin);
freopen("mountain.out","w",stdout);
n=read();
for (reg int i=1;i<=n;i++)
{
X[i]=read(); Y[i]=read();
if (Y[i]>=Y[rt]) rt=i;
}
solve(); flip(); solve(); dp();
for (reg int i=n;i>=1;i--)
write(ans[i]),putchar(10);
return 0;
}