Codeforces GYM102700 I. Incredible photography 题解

446 阅读2分钟

题目链接

思路

假设现在从第ii个点出发,定义lplp如下:

a[lp]>a[i]k(lp,i):a[k]<=a[lp]若存在llp<lp使得a[lp]=a[llp]:k(llp,lp):a[k]>a[lp]a[lp]>a[i]\\ \forall k\in(lp,i):a[k]<=a[lp]\\ 若存在llp<lp使得a[lp]=a[llp]:\exist k\in(llp,lp):a[k]>a[lp]

简单来说,就是在ii左边找到第一个高于a[i]a[i]的楼(高度为a[lp]a[lp]),若继续往左仍有高度为a[lp]a[lp]的楼且该楼与ii之间没有更高的楼阻挡,就继续往左取。对称地定义rprp
那么最优解就是在a[lp],a[rp]a[lp],a[rp]中取较矮的楼转移,若二者高度相同,则都进行转移,在得到的答案中取最大值。简单的证明:若先转移到较高的楼就失去了向较矮的楼转移获得更大答案的机会,而先转移到较矮的楼仍然可以向原先较高的楼转移,所以是最优的。转移的过程可以使用记忆化搜索。
如何在搜索中得到lp,rplp,rp决定了该题的复杂度。首先用单调栈预处理出两个数组pre[N]pre[N]nxt[N]nxt[N]pre[i]pre[i]为左侧第一个高于或等于当前楼的位置,nxt[i]nxt[i]为右侧第一个高于或等于当前楼的位置。在搜索的时候就可以借助这两个数组顺藤摸瓜一路找到lplprprp。复杂度可能比较玄学,但是能过。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
using namespace std;
const int N=1E5+10;
const int long long INF=0x7ffffffffff;
typedef long long ll;
int n;
ll a[N],pre[N],nxt[N];;
ll ans[N];
stack<ll> s;
ll dfs(ll cur)
{
	if(ans[cur]!=-INF)
		return ans[cur];
	ans[cur]=0;
	ll lp=pre[cur],rp=nxt[cur];
	while(lp && (a[lp]==a[cur] || a[lp]==a[pre[lp]]))
		lp=pre[lp];
	while(rp && (a[rp]==a[cur] || a[rp]==a[nxt[rp]]))
		rp=nxt[rp];
	if(a[rp]<a[lp])
	{
		ans[cur]=rp-cur;
		ans[cur]+=dfs(rp);
	}
	else if(a[rp]>a[lp])
	{
		ans[cur]=cur-lp;
		ans[cur]+=dfs(lp);
	}
	else if(lp)
		ans[cur]=max(dfs(lp)+cur-lp,dfs(rp)+rp-cur);
	return ans[cur];
}
int main()
{
	scanf("%d",&n);
	rep(i,1,n)
		scanf("%lld",a+i);
	rep(i,0,n)
		ans[i]=-INF;
	rep(i,1,n)
	{
		int j=i;
		while(a[i]==a[j+1])
			++j;
		while(!s.empty() && a[i]>=a[s.top()])
		{
			nxt[s.top()]=j;
			s.pop();
		}
		rep(k,i,j)
			s.push(k);
		i=j;
	}
	while(!s.empty())
	{
		nxt[s.top()]=0;
		s.pop();
	}
	for(int i=n;i>=1;--i)
	{
		int j=i;
		while(a[i]==a[j-1])
			--j;
		while(!s.empty() && a[i]>=a[s.top()])
		{
			pre[s.top()]=j;
			s.pop();
		}
		rep(k,j,i)
			s.push(k);
		i=j;
	}
	while(!s.empty())
	{
		pre[s.top()]=0;
		s.pop();
	}
	// rep(i,1,n)
		// cout<<pre[i]<<" "<<nxt[i]<<endl;
	a[0]=INF;
	rep(i,1,n)
	{
		if(ans[i]!=-INF)
			continue;
		ans[i]=dfs(i);
	}
	rep(i,1,n)
		printf("%lld ",ans[i]);
	printf("\n");
}