\(\text{Description}\)

传送门

\(\text{Solution}\)

解法一

是考场上自己的想法,观察到 \(a\) 数组很小(以为是暗示复杂度是 \(\mathcal O(n*a)\))。

我们令到第 \(i\) 号位的最大值为 \(f[i]\),就有 \(\text{DP}\) 式:

\[f[i]=\max(f[j]+(i-j)\times a[i]) \]

容易发现其实是求 \(\max(f[j]-j\times a[i])\)。然后因为 \(a[i]\) 种类有限,可以每次加入一个数时枚举 \(a[i]\),再计算在那个情况下的最大值。

详见代码。

解法二

你会发现相当于从 \(j\)\(i\) 的贡献就是将 \([j+1,i]\) 区间的 \(a\) 全赋值为 \(a[i]\) 然后求 \(\sum a[i]\)

我们倒着分析,\(a[n]\) 是一定被选取的,那么我们贪心地选离 \(n\) 最近的 \(j\),其中 \(a[j]>a[n]\),将 \(j\) 作为新的赋值点,这样一定是最优的。

时间复杂度 \(\mathcal O(n)\)

解法三

斜率优化赛高!!!

用上文的 \(\text{DP}\) 式,设 \(j\) 状态优于 \(k\) 状态且 \(j>k\)。一阵乱推可得:

\[\frac {f[j]-f[k]}{j-k}<a[i] \]

但这不能直接用单调队列,因为 \(a[i]\) 没有单调性。

所以,

首先我们肯定要维护一个下凸包,然后在这个凸包里二分刚好小于 \(a[i]\) 的那个斜率,就是我们要的答案。

时间复杂度 \(\mathcal O(n \log n)\)

\(\text{Code}\)

#include <cstdio>
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)

typedef long long ll;

int read() {
	int x=0,f=1; char s;
	while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
	while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return x*f;
} 

void write(ll x) {
	if(x<0) return (void)(putchar('-'),write(-x));
	if(x>9) write(x/10);
	putchar(x%10^48);
}

void print(ll x,char y) {
	write(x),putchar(y);
}

#include <iostream>
using namespace std;

const int maxn=1e5+5;

int n;
ll pre[55],ans;

int main() {
	freopen("game.in","r",stdin); freopen("game.out","w",stdout);
	int x;
	n=read();
	rep(i,1,n) {
		x=read();
		ans=pre[x]+1ll*x*i;
		rep(j,0,50) pre[j]=max(pre[j],ans-1ll*i*j);
	}
	print(ans,'\n');
	return 0;
}
posted on 2020-11-23 18:02  Oxide  阅读(64)  评论(0编辑  收藏  举报