题解 P4513 【小白逛公园】

Obito

2019-10-01 14:45:52

Solution

#### 先分析一下题目大概意思。意思很简单 #### 你需要完成两种操作。 #### 1.将X的点进行更改 #### 2.求a到b区间的最大子段和 ### 看到这里,脑袋里第一眼浮现的解题方法就是线段树。(不会线段树左转[线段树](https://www.luogu.org/problem/P3372) ) ### why?? ### 单点修改,区间查询,线段树不就可以处理吗? #### 现在问题来了:该怎么处理最大子段和呢? 我们不难想到,一个区间的最大子段和源于三个地方; ### 1.左边界以左的最大子段和。 ### 2.右边界以右的最大子段和。 ### 3.中间向两边拓展的最大子段和。 ### 思路已经有了具体看代码。 ``` #include<bits/stdc++.h> #define int long long using namespace std; const int maxn=(500000+10)*4; int num[maxn],addval[maxn]; struct node{ int lx,sum,rx,ans,l,r; //lx:左边界以左的最大子段和; //rx:右边界以右的最大子段和; //ans:最后的最大子段和 //sum:区间总和 //l:左边界,r 右边界 }tree[maxn]; void build(int k,int left,int right){//建树 tree[k].l=left; tree[k].r=right;//左右范围 if(left==right){ tree[k].sum=tree[k].lx=tree[k].rx=tree[k].ans=num[left];//初始值 return ; } int mid=(left+right)>>1;//二分拓展 build(k*2,left,mid); build(k*2+1,mid+1,right); tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;//求区间总和 tree[k].lx=max(tree[k*2].lx,tree[k*2].sum+tree[k*2+1].lx);//左边界最大值有两种可能:1.左子树的最大子段和,2.左子树的总和,加上右子树的左边界起始的最大子段和。 tree[k].rx=max(tree[k*2+1].rx,tree[k*2+1].sum+tree[k*2].rx);//右子树同理,可以自己推一下 tree[k].ans=max(max(tree[k*2].ans,tree[k*2+1].ans),tree[k*2].rx+tree[k*2+1].lx);//最终值比较 } node query(int k,int lt,int rt){//查询 if(lt<=tree[k].l&&rt>=tree[k].r)return tree[k];//区间被包含,全部返回 node a,b,ans;//取出左子树,右子树 a.lx=a.rx=a.ans=a.ans=-2047483647; b.lx=b.rx=b.ans=b.ans=-2047483647; a.sum=b.sum=0; ans.ans=-2047483647; ans.sum=0; int mid=(tree[k].l+tree[k].r)>>1; if(lt<=mid){ a=query(k*2,lt,rt); ans.sum+=a.sum; } if(rt>=mid+1){ b=query(k*2+1,lt,rt); ans.sum+=b.sum; } ans.ans=max(a.rx+b.lx,max(a.ans,b.ans));//和建树时的想法相同 ans.lx=max(a.lx,a.sum+b.lx); ans.rx=max(b.rx,b.sum+a.rx); if(lt>mid){ ans.lx=max(ans.lx,b.lx); } if(rt<mid){ ans.rx=max(ans.rx,a.rx); } return ans; } void modify(int k,int lt,int rt,int qx,int val){//更改,和建树想法差不多 if(qx<lt||qx>rt)return ; else if(lt==qx&&rt==qx){ tree[k].ans=tree[k].lx=tree[k].rx=tree[k].sum=val; return ; } else { int mid=lt+(rt-lt)/2; modify(k*2,lt,mid,qx,val); modify(k*2+1,mid+1,rt,qx,val); tree[k].sum=tree[k*2].sum+tree[k*2+1].sum; tree[k].lx=max(tree[k*2].lx,tree[k*2].sum+tree[k*2+1].lx); tree[k].rx=max(tree[k*2+1].rx,tree[k*2+1].sum+tree[k*2].rx); tree[k].ans=max(max(tree[k*2].ans,tree[k*2+1].ans),tree[k*2].rx+tree[k*2+1].lx); return ; } } signed main(){//主函数 int n,m; cin>>n>>m; for(int i=1;i<=n;i++) scanf("%lld",&num[i]); build(1,1,n); for(int i=1;i<=m;i++){ int pre,x,y,z; scanf("%lld%lld%lld",&pre,&x,&y); if(pre==2){ modify(1,1,n,x,y); } else { if(x>y)swap(x,y); node a=query(1,x,y); cout<<a.ans<<endl; } } return 0; } /* 5 3 1 2 -3 4 5 1 2 3 2 2 -1 1 3 2 */ ``` ### 代码就是这样,主要是码字较多。 #### 看懂之后,推荐两道题 ### 1.[好一个一中腰鼓](https://www.luogu.org/problem/P2253) ### 2.[你能回答这些问题吗?(一样的)](https://www.acwing.com/problem/content/description/246/)