\(\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;
}