CF1359D Yet Another Yet Another Task
题解的简单写法。
看到值域很小,而且限制我们快速求出贡献的是区间最大值,我们不妨枚举这个区间最大值 \(i\) ,位置是\(p_i\)。
假如 \(i\) 只有一个的话,那么 \(r\) 一定大于等于 \(p_i\),\(l\) 一定小于等于 \(p_i\)。
假如有多个的话,运用扫描线的思想,然后发现到 \(r\) 到 \(p_{i,k}\) , \(l\) 就可以到 \(p_{i,k}\) 。
然后线性维护 \(s_l\) 最小值,枚举 \(r\) 即可(\(l\) 可以更新到 \(r\) 左边第一个 \(p_{i,k}\),类似双指针)。
但是你发现这样求的是最大值 \(\ge x\) 的贡献,因为你不能保证 \(p_{i,k}, p_{i,k+1}\) 之间的数都小于 \(i\)。
虽然这样求的有交集,但是总的并集是能够等价于原问题集合的。(最大值\(\le 1, \le 2, \le 3\) 并起来就是 \(\{1, 2, 3\}\))。
这篇题解求得是最大值\(\le x\) 的贡献,也是一样的。
所以我们发现,枚举最值的时候,通常是满足不等关系。
貌似没有线段树做法。
记\(s\)为\(a\)的前缀和数组。
对于一个确定的右端点 \(r\) 和左端点 \(l\),它对于答案的贡献是 \(s_r-s_{l-1}-max\{a_i\},l\le i\le r\) ,如果枚举右端点,令 \(c_l=s_{l-1}+max\{a_i\},l\le i\) 。那么其实就是要求 \(1\le k\le r-1\) 的 \(min\{c_k\}\)。
线段树维护 \(c_k\) 即可。
用单调栈求出 \(a_i\) 左边第一个大于自己的数 \(a_p\),然后把 \([p,i-1]\) 的 \(max\{a_i\}\) 覆盖成 \(a_r\) 即可。
然后你发现要维护 \(c\) ,你得维护 \(s\) 的最小值,和 \(max\{a_i\}\) 。
时间复杂度 \(O(nlogn)\) 。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N], n, s[N];
int stk[N], top;
struct Node {
int l, r;
int s, a, v; // s[i] + a[i]
int tag;
}tr[N << 2];
void pushup(int u) {
tr[u].v=min(tr[u<<1].v,tr[u<<1|1].v);
tr[u].s=min(tr[u<<1].s,tr[u<<1|1].s);
}
void pushdown(int u) {
if (tr[u].tag != 2e9) {
tr[u<<1].a=tr[u].tag,tr[u<<1].v=tr[u<<1].s+tr[u].tag;
tr[u<<1|1].a=tr[u].tag,tr[u<<1|1].v=tr[u<<1|1].s+tr[u].tag;
tr[u<<1|1].tag=tr[u<<1].tag=tr[u].tag;
tr[u].tag = 2e9;
}
}
void build(int u,int l,int r) {
tr[u]={l,r};
tr[u].tag=2e9;
if (l==r) {
tr[u].a=1e9;
tr[u].s=s[l];
tr[u].v=tr[u].a+tr[u].s;
tr[u].tag=2e9;
return;
}
int mid = l +r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int L,int R,int k) {
if (tr[u].l>=L&&tr[u].r<=R) {
tr[u].a=k;
tr[u].v=tr[u].s+k;
tr[u].tag=k;
return ;
}
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
if (L<=mid) modify(u<<1,L,R,k);
if (R>mid) modify(u<<1|1,L,R,k);
pushup(u);
}
int query(int u,int L,int R) {
if (tr[u].l>=L&&tr[u].r<=R)
return tr[u].v;
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
int res=2e9;
if (L<=mid) res=min(res,query(u<<1,L,R));
if (R>mid) res=min(res,query(u<<1|1,L,R));
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i];
int ans = 0;
build(1,0,n);
for (int i = 1; i <= n; i ++ ) {
while (top && a[stk[top]] <= a[i]) top -- ;
// [stk[top], i-1]拍成 a[i]+s[k]
modify(1,stk[top],i-1,a[i]);
ans=max(ans,s[i]-query(1,0,i-1));
stk[ ++ top] = i;
}
cout << ans << endl;
return 0;
}