BZOJ1345:[Baltic2007]序列问题
浅谈栈:https://www.cnblogs.com/AKMer/p/10278222.html
题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=1345
假设当前序列是单调的,那么显然答案就是从低往高合并的权值和。
假设\(a\leqslant 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\n",ans);
return 0;
}