codeforces round#783 div2 D
思路:
首先考虑前缀和为负数和0,肯定是每次只考虑一个数字最优;
然后考虑dp f[i]=max(f[j]+i-j),其中j+1到i的和为正数,
就是在前i个里找前缀和小于si并且最大的f[j],因为f[j]中j已经选择,
所以是j+1到i,使用树状数组维护前缀和就可以查询,更新的时候可以
赋值为f[j]-j,这样求出来的最大值再加i就是f[i]
代码:
const int N = 500010;
int c[N];
int ask(int x) {
int res = -inf;
for (;x;x -= lowbit(x)) res = max(res, c[x]);
return res;
}
void add(int x, int y) {
for (;x < N;x += lowbit(x)) c[x] = max(c[x], y);
}
int f[N], a[N], s[N];
vector<int> v;
int find(int x) {
return lower_bound(all(v), x) - v.begin() + 1;
}
void solve() {
int n = read();
v.clear();
for (int i = 1;i <= n;i++) a[i] = read(), s[i] = s[i - 1] + a[i], v.push_back(s[i]);
sort(all(v));
v.erase(unique(all(v)), v.end());
for (int i = 0;i <= n + 10;i++) c[i] = -inf;
int ans = 0;
f[0] = 0;
for (int i = 1;i <= n;i++) f[i] = -inf;
for (int i = 1;i <= n;i++) {
int x = find(s[i]);
int t = ask(x - 1);
int val = 0;
if (a[i] > 0) val = 1;
else if (a[i] == 0) val = 0;
else val = -1;
int d = -inf;
if (s[i] > 0) d = i;
f[i] = max({ f[i - 1] + val, t + i,d });
add(x, f[i] - i);
}
printf("%lld\n", f[n]);
clean();
}