BZOJ1345:[Baltic2007]序列问题

浅谈栈:https://www.cnblogs.com/AKMer/p/10278222.html

题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=1345

假设当前序列是单调的,那么显然答案就是从低往高合并的权值和。

假设(aleqslant b leqslant c),那么合并(a,b)然后合并(b,c)的代价是(b+c),显然要比合并(b,c)再合并(a,c)的代价(c+c)要优。

如果当前序列不单调,那么必然存在一个位置(pos)满足(a[pos-1]geqslant a[pos] leqslant a[pos+1])。我们只需要把(a[pos])(a[pos-1],a[pos+1])中值较小的那个合并即可。可以证明,能与(a[pos])合并的最小值就是(min(a[pos-1],a[pos+1])),如果(a[pos-1])或者(a[pos+1])之前与其它数合并过,并且发生了改变,那么改变之后的数值显然是大于(min(a[pos-1],a[pos+1]))的。所以我们只需要维护一个单调栈,发现这种情况判断更新答案即可。最后会剩下一个单调递减的序列,我们再按照第一种情况处理就行。

时间复杂度:(O(n))

空间复杂度:(O(n))

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=1e6+5,inf=2e9+5;

ll ans;
int n,top;
int stk[maxn];

int read() {
	int x=0,f=1;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
	return x*f;
}

int main() {
	n=read();
	stk[0]=inf;
	for(int i=1;i<=n;i++) {
		int x=read();
		while(top&&stk[top]<=x)
			ans+=min(stk[top-1],x),top--;
		stk[++top]=x;
	}
	top--;
	while(top)ans+=stk[top],top--;
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/AKMer/p/10284357.html