分析
我就简单口糊一下吧。
这题其实很像一个最大子段。
我们可以考虑用线段树维护区间最大子段和,不会的做这题。
先枚举右端点,然后我们求的就是 [ 1 , r ] [1,r] [1,r] 中的最大价值。
我们假设 [ 1 , r − 1 ] [1,r-1] [1,r−1] 已经消除了影响,可以直接求最大子段和。对于新碰到的电影编号 x x x,我们就要消除它的影响。
无非就是将上一个与 x x x 编号相同的位置(即第几天)上的价值改成负的,然后把上上个与 x x x 编号相同的位置上的价值改成0的(上上个前面相同编号位置的价值已经是0)。
这样就消除了影响,最后取max就可以了。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
Code(无read)
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6;
const ll inf = 1ll << 63;
ll ans = -inf;
int n, m, a[N|1], w[N|1], pre[N|1], lst[N|1];
struct SegamentTree {
static const int MAX = N;
struct TreeNode {
ll val, lval, rval, sum;
} tr[MAX<<2];
inline TreeNode pushup(TreeNode ls, TreeNode rs) {
TreeNode rt;
rt.val = max(ls.val, max(rs.val, ls.rval+rs.lval));
rt.lval = max(ls.lval, ls.sum+rs.lval);
rt.rval = max(rs.rval, rs.sum+ls.rval);
rt.sum = ls.sum + rs.sum;
return rt;
}
void update(int k, int l, int r, int x, int t) {
if(l == r) {
tr[k] = {t, t, t, t};
return;
}
int mid = l + (r - l >> 1);
if(x <= mid) update(k<<1, l, mid, x, t);
else update(k<<1|1, mid+1, r, x, t);
tr[k] = pushup(tr[k<<1], tr[k<<1|1]);
}
} A;
signed main() {
n = read(), m = read();
for(int i = 1; i <= n; ++i)
a[i] = read();
for(int i = 1; i <= m; i++)
w[i] = read();
for(int i = 1; i <= n; ++i) {
pre[i] = lst[a[i]], lst[a[i]] = i;
if(pre[i]) A.update(1, 1, n, pre[i], -w[a[i]]);
if(pre[pre[i]]) A.update(1, 1, n, pre[pre[i]], 0);
A.update(1, 1, n, i, w[a[i]]);
ans = max(ans, A.tr[1].val);
}
printf("%lld", ans);
return 0;
}