题目描述
补题记录:
思路:两颗线段树:tp维护最小前缀和,tn维护最小后缀和
要计算[L,R]上的最大区间和,(其中 L<=i,R<=i+ki,L<=R)
只需要求出区间[i-ki-1,i-1]的最小前缀和 和 区间[i+1,i+ki+1]的最小后缀和
数组总和减去这两部分的值就是第i个位置上的最大wonderful interval
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1000000+10;
const int INF=0x3f3f3f3f;
int a[maxn];
ll pre[maxn],nxt[maxn];
int k[maxn];
int N;
//线段树单点更新区间求和
struct Tree{
ll x[maxn];
//初始化
void init(int x){
N=1;
while(N<=x*2) N*=2;
}
//单点更新
void update(int k,int q){
k+=N-1;
x[k]=q;
while(k){
k=(k-1)/2;
x[k]=min(x[k*2+1],x[k*2+2]);
}
}
//区间查询
ll query(int a,int b,int l,int r,int k){
if(r<a || b<l) return INF; //处理边界
if(a<=l && r<=b) return x[k]; //处理边界
else{
ll vl=query(a,b,l,(l+r)/2,k*2+1);
ll vr=query(a,b,(l+r)/2+1,r,k*2+2);
return min(vl,vr);
}
}
}tp,tn;
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&k[i]);
tp.init(n);
//前缀和 建线段树
ll sum=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum+=a[i];
tp.update(i,sum);
}
//后缀和 建线段树
sum=0;
for(int i=n;i>=1;i--){
sum+=a[i];
tn.update(i,sum);
}
//i从1~n sum为数组总和 定义的最大区间值 就=sum-最小前缀-最小后缀
ll ans=0;
for(int i=1;i<=n;i++){
ans+=sum;
ans-=tp.query(max(i-k[i]-1,0),max(i-1,0),0,N-1,0);
ans-=tn.query(min(i+1,n+1),min(i+k[i]+1,n+1),0,N-1,0);
}
printf("%lld
",ans);
return 0;
}