Max Sum|线段树单点更新区间求和,维护前后缀和最小值

K题地址

题目描述

补题记录:

思路:两颗线段树: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\n",ans);
    return 0;
}
posted @ 2019-12-26 22:04  fishers  阅读(390)  评论(0编辑  收藏  举报