2019牛客暑期多校训练营(第四场)C sequence【线段树】【单调栈】
题意:
给你2个长度为n的区间 a区间和b区间
区间的值为b区间之和乘以a区间的最小值,要你求出值最大的区间
题目链接:
https://ac.nowcoder.com/acm/contest/884/C
题解:
南昌邀请赛 I题原题QAQ
记录下a数组每个点以他为最小值的区间最左边是哪个点,最右边是哪个点,用单调栈进行操作 r[i]表示右边界 l[i]表示左边界
用线段树维护前缀和,如果a[i]为非负树,那么b区间和越大越好,区间值就为(sum[r[i]]-sum[l[i]-1])*a[i]
如果为负数,尽量找区间和最小, 那么要在i之前找前缀和最大的位置,在i之后找前缀和最小的位置,最小减最大极为最优
如果i之前前缀和为负数,那直接把左端点设在i最优
AC_code:
#include<bits/stdc++.h>
using namespace std;
#define maxn 3000005
#define ll long long
#define inf 0x3f3f3f3f
int n,l[maxn],r[maxn];
ll a[maxn], pre[maxn], sum[2][maxn<<2];
ll b[maxn];
void pushup(int rt) {
sum[0][rt] = max(sum[0][rt<<1], sum[0][rt<<1|1]);
sum[1][rt] = min(sum[1][rt<<1], sum[1][rt<<1|1]);
}
void build(int l, int r, int rt) {
if(l == r) {
sum[0][rt] = sum[1][rt] = pre[l];
return ;
}
int mid = (l+r)>>1;
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
pushup(rt);
}
ll qmax(int L, int R, int l, int r, int rt) {
if(L <= l && R >= r) {
return sum[0][rt];
}
int mid = (l+r)>>1;
ll ans = -inf;
if(L <= mid) {
ans = max(ans,qmax(L, R, l, mid, rt<<1));
}
if(R > mid) {
ans = max(ans, qmax(L, R, mid+1, r, rt<<1|1));
}
return ans;
}
ll qmin(int L, int R, int l, int r, int rt) {
if(L <= l && R >=r ) {
return sum[1][rt];
}
int mid = (l+r)>>1;
ll ans = inf;
if(L <= mid) {
ans = min(ans,qmin(L, R, l, mid, rt<<1));
}
if(R > mid) {
ans = min(ans, qmin(L, R, mid+1, r, rt<<1|1));
}
return ans;
}
int main() {
cin>>n;
for(int i = 1; i <= n; i++) {
scanf("%lld", &b[i]);
}
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
pre[i] = pre[i-1] + a[i];
}
build(1, n, 1);
stack<int>s;
for(int i = 1; i <= n; i++) {
while(s.size() && b[s.top()] >= b[i]) {
s.pop();
}
if(s.empty()) {
l[i] = 1;
} else {
l[i] = s.top() + 1;
}
s.push(i);
}
while(!s.empty()) {
s.pop();
}
for(int i = n; i >= 1; i--) {
while(s.size() && b[s.top()] >= b[i]) {
s.pop();
}
if(s.empty()) {
r[i]=n;
} else {
r[i] = s.top() - 1;
}
s.push(i);
}
ll ans = -inf;
for(int i = 1; i <= n; i++) {
if(b[i] >= 0) {
ans = max(ans, (pre[r[i]] - pre[l[i]-1]) * b[i]);
} else {
ll maxx, minn;
maxx = qmax(l[i], i, 1, n, 1);
if(maxx < 0) {
maxx = 0; //前面区间 的最大值如果 小于0则不去直接以i为左边界
}
minn = qmin(i, r[i], 1, n, 1);
ans = max(ans, (minn - maxx) * b[i]);
}
}
cout << ans << endl;
return 0;
}