[bzoj1345][Baltic2007]序列问题_单调栈

bzoj-1345 Baltic-2007 序列问题

题目大意:对于一个给定的序列a1,…,an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai和ai+1用一个元素max(ai,ai+1)替代,这样得到一个比原来序列短的新序列。这一操作的代价是max(ai,ai+1)。进行n-1次该操作后,可以得到一个长度为1的序列。我们的任务是计算代价最小的reduce操作步骤,将给定的序列变成长度为1的序列。

数据范围:$1\le n\le 10^6$,$0\le a_i\le 10^9$。


想法

这个题的考虑方法比较经典

就是对于每一个操作,把它按照某种规则分类。

然后分类之后比较好统计什么的。

像$CF1151E$一样的类型。

好,单独看这个题。

我们把每次合并当做一个小数被吃了。

所以我们就统计没个数被吃了那次的答案和即可。

假设$pre[i]$和$nxt[i]$分别表示第$i$个数前面比它大的第一个和后面比它大的第一个。

这个数一定是被前面的某个或者后面的某个吃掉。

如果这两个在当前数被吃掉之前被吃掉了,那一定没有当前数先被吃掉优。

故此我们用单调栈求出每个数前面第一个比它大的,和后面第一个比它大的。

每个数的答案就是$min(a_{pre_i},a_{nxt_i})$。

注意最大值没有贡献。

代码

#include <bits/stdc++.h>
#define inf (1 << 30)
#define N 1000010 
using namespace std;
int a[N],q[N],pre[N],nxt[N];
typedef long long ll;
ll ans=0;
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {int x=0,f=1; char c=nc(); while(c<48) {if(c=='-') f=-1; c=nc();} while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x*f;}
int main()
{
	int n=rd();
	for(int i=1;i<=n;i++) a[i]=rd();
	int top=0;
	for(int i=1;i<=n;i++)
	{
		while(top && a[i] > a[q[top]]) top -- ;
		pre[i] = q[top];
		q[++top]=i;
	}
	top=0;
	for(int i=n;i;i--)
	{
		// printf("top : %d\n",top);
		while(top && a[i] > a[q[top]]) top -- ;
		nxt[i] = q[top];
		q[++top]=i;
	}
	// for(int i=1;i<=n;i++) cout << pre[i] << ' ' << nxt[i] << endl ;
	a[0]=inf;
	for(int i=1;i<=n;i++) if(pre[i] || nxt[i]) ans += min(a[pre[i]],a[nxt[i]]);
	cout << ans << endl ;
	return 0;
}

小结:这种解题方法比较独特,类型题好像也挺多的。看能不能想到吧。

posted @ 2019-06-20 20:54 JZYshuraK_彧 阅读(...) 评论(...) 编辑 收藏