[BOI2007] Sequence

题目描述

对于一个给定的序列a1, …, an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai和ai+1用一个元素max(ai,ai+1)替代,这样得到一个比原来序列短的新序列。这一操作的代价是max(ai,ai+1)。进行n-1次该操作后,可以得到一个长度为1的序列。

我们的任务是计算代价最小的reduce操作步骤,将给定的序列变成长度为1的序列。

输入输出格式

输入格式:

 

第一行为一个整数n( 1 <= n <= 1,000,000 ),表示给定序列的长度。

接下来的n行,每行一个整数ai(0 <=ai<= 1, 000, 000, 000),为序列中的元素。

 

输出格式:

 

只有一行,为一个整数,即将序列变成一个元素的最小代价。

 

输入输出样例

输入样例#1: 
3
1
2
3
输出样例#1: 
5

说明

提示 30%的测试数据 n<=500; 50%的测试数据 n <= 20,000。

 

 

    发现只能是相邻的合并,并且一个数会在第一次与比它大的数合并时候消失,并且产生那个数的贡献。

所以我们就找一下一个数两边第一个大于它的数,加一下两者中的较小值即可。

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1000005;
ll sum=0;
int L[maxn],R[maxn];
int n,a[maxn],s[maxn],tp=0;

inline int read(){
	int x=0; char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
	return x;
}

int main(){
	scanf("%d",&n),fill(L+1,L+n+1,-1),fill(R+1,R+n+1,-1);
	for(int i=1;i<=n;i++) a[i]=read();
	
	for(int i=1;i<=n;i++){
		while(tp&&a[s[tp]]<=a[i]) tp--;
		if(tp) L[i]=a[s[tp]];
		s[++tp]=i;
	}
	tp=0;
	for(int i=n;i;i--){
		while(tp&&a[s[tp]]<=a[i]) tp--;
		if(tp) R[i]=a[s[tp]];
		s[++tp]=i;
	}
	
	for(int i=1;i<=n;i++) if(L[i]>=0||R[i]>=0){
		if(L[i]<0) sum+=(ll)R[i];
		else if(R[i]<0) sum+=(ll)L[i];
		else sum+=(ll)min(L[i],R[i]); 
	}
	
	printf("%lld\n",sum);
	return 0;
}

  

posted @ 2018-04-12 20:57  蒟蒻JHY  阅读(253)  评论(0编辑  收藏  举报