AcWing245你能回答这些问题吗(线段树+逻辑思维)

题目地址https://www.acwing.com/problem/content/246/

题目描述

给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:

1、“1 x y”,查询区间 [x,y] 中的最大连续子段和

2、“2 x y”,把 A[x] 改成 y。

对于每个查询指令,输出一个整数表示答案。

输入格式

第一行两个整数N,M。

第二行N个整数A[i]。

接下来M行每行3个整数k,x,y,k=1表示查询(此时如果x>y,请交换x,y),k=2表示修改。

输出格式

对于每个查询指令输出一个整数表示答案。

每个答案占一行。

数据范围

N500000,M100000

题解:通过题目我们可以知道需要完成的单点修改和查询区间信息,这是一个典型的线段树问题。线段树是可以根据子节点的区间信息推出父节点的区间信息,但是这里如果只是单纯的知道两个子节点的最大连续区间和是并不能直接推算出父节点的最大连续区间和。如果要想推算出父节点的最大连续区间和,需要分3种情况:(最后父节点的tmax就是下面三个数据的最大值)

1:父节点的最大连续区间正好全部在左儿子中,那么只要知道左儿子的最大连续区间和就可以了

2:父节点的最大连续区间正好全部在右儿子中,那么只要知道右儿子的最大连续区间和就可以了

3:父节点的最大连续区间横跨左右儿子区间,那么我们需要知道的是左儿子的最大后缀和和右儿子的最大前缀和,二者相加就是父节点的最大连续区间和

通过上面的三种情况,我们首先确定了结构体中需要包含l,r(区间左右端点),tmax(区间最大连续和),lmax(区间最大前缀和),rmax(区间最大后缀和)

现在确定了tmax,可以通过儿子结点来确定,那么接下来看lmax,rmax是否可以通过儿子结点来确定。确定前缀和分为两种情况:

1:父节点的最大前缀和区间正好全部在左儿子区间中,那么只需要知道左儿子的最大前缀和就可以了

2:父节点的最大前缀和区间横跨左右儿子区间,那么需要知道的是左儿子的区间和和右儿子的最大前缀和

通过上面的两种情况,我们确定了结构体中需要包含l,r(区间左右端点),tmax(区间最大连续和),lmax(区间最大前缀和),rmax(区间最大后缀和),sum(区间和)

现在父节点的tmax,lmax,rmax,都可以直接通过儿子结点拉计算出来,而sumyekeyi8轻易通过儿子结点计算出来,那么现在就可确定结构体中的元素了。

剩下的就是写build(),modify(),query(),pushup()这四个函数了,虽然也不是很好写,但是已经属于可以完成的范围内了。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
struct node{
    int l,r;
    int tmax;//区间大连续的最大和
    int lmax,rmax;//区间最大的前缀和、后缀和
    int sum;//区间的总和 
    node (){
        tmax=0;
        lmax=0;
        rmax=0;
        sum=0;
    }
}tr[N*4];

//这里的pushup的形参与一般情况写的不一样,这是因为在query中需要通过子区间来求的目标区间的问题时需要用到这种写法 
struct node pushup(struct node &u,struct node &l,struct node &r){
    u.sum=l.sum+r.sum;
    u.lmax=max(l.lmax,l.sum+r.lmax);
    u.rmax=max(l.rmax+r.sum,r.rmax);
    u.tmax=max(max(l.tmax,r.tmax),l.rmax+r.lmax);
    return u;
}

void build(int u,int l,int r){
    tr[u].l=l,tr[u].r=r;
    if(l==r){
        int x;cin>>x;
        tr[u].lmax=x;
        tr[u].rmax=x;
        tr[u].sum=x;
        tr[u].tmax=x;
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
    return ;
}
void show(struct node a){
    cout<<a.l<<" "<<a.r<<" "<<a.lmax<<" "<<a.rmax<<" "<<a.tmax<<" "<<a.sum<<endl;
}
struct node query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r) {
        return tr[u];
    }
    int mid=tr[u].l+tr[u].r>>1;
    struct node a1,a2;
    if(l<=mid) a1=query(u<<1,l,r);
    if(r>mid) a2=query(u<<1|1,l,r);
    struct node a;
    if(l<=mid&&r>mid) return pushup(a,a1,a2);
    else if(l<=mid) return a1;
    else return a2;
}

void modify(int u,int x,int d){
    if(tr[u].l==x&&tr[u].r==x){
        tr[u].lmax=d;
        tr[u].rmax=d;
        tr[u].sum=d;
        tr[u].tmax=d;
        return ;
    }
    int mid=tr[u].l+tr[u].r>>1;
    if(x<=mid) modify(u<<1,x,d);
    if(x>mid) modify(u<<1|1,x,d);
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
    return ;
}

int main(){
    int n,m;cin>>n>>m;
    build(1,1,n);
    int k,x,y;
    while(m--){
        scanf("%d%d%d",&k,&x,&y);
        if(k==1){
            if(x>y) swap(x,y);
            struct node a=query(1,x,y);
            cout<<a.tmax<<endl;
        }
        else {
            modify(1,x,y);
        }
    }
    return 0;
}

写于:202/8/27  11:00

AcWing245你能回答这些问题吗(线段树+逻辑思维)

上一篇:Delphi WinAPI CompareString 和 CompareStringEx 比较两个标识符指定的两个字符串函数


下一篇:DevExpress WPF v20.1:全新升级Map、Pivot Grid控件功能