【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;
}
posted @ 2021-02-17 16:26  stoorz  阅读(45)  评论(0编辑  收藏  举报