题目描述
对于一个给定的序列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; }
原文地址:https://www.cnblogs.com/JYYHH/p/8810408.html
时间: 2024-09-28 12:46:39