マックス・サム|ツリーラインシングルポイントの更新間隔の和、最小のメンテナンスやプレフィックスとサフィックス

Kアドレス質問

タイトル説明

富栄チタンレコード:

思考の2つのセグメントツリー:TP最小限のメンテナンスや接頭辞、接尾辞及び最小保守TN
最大区間[L、R]とを計算する(L <= I、R <= I + KI、L <= R)
唯一必要な区間[I-KI-1、I -1] とプレフィックスと最小間隔[I + 1、I + KI + 1] とサフィックスの最小
アレイの2つの部分を差し引いた合計値は、i番目です場所の最大素晴らしい間隔

#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;
}

おすすめ

転載: www.cnblogs.com/fisherss/p/12104701.html