【BZOJ1345】[Baltic2007] 序列问题(单调栈大水题)

点此看题面

大致题意: 你可以花费(max{a_i,a_{i+1}})的代价合并相邻两个元素为(max{a_i,a_{i+1}}),求把序列合成一个点的最小代价。

贪心

贪心地去思考,我们每次肯定选择序列中最小的数,把它和它两侧中较小的那个数合并。

这样肯定是最优的,但却并不好维护在区间中删去某一个数。

然后我们发现,实际上只需要一个数比它两侧的数都小,我们就可以按上面的方式去合并,那么问题就简单了。

单调栈

我们维护一个单调递减的单调栈。

每当栈顶元素小于当前元素,我们就将栈顶元素与栈顶之下的元素和当前元素中较小的那一个合并,然后就可以将其弹出了。

所以说这道题真是道大水题。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define LL long long
using namespace std;
int n,S[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
int main()
{
	RI i,x,T=0;LL t=0;for(F.read(n),i=1;i<=n;++i)
		{F.read(x);W(T&&S[T]<=x) t+=T^1?min(S[T-1],x):x,--T;S[++T]=x;}//单调栈
	--T;W(T) t+=S[T--];return printf("%lld",t),0;//清空栈内元素计算贡献
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1345.html