zoukankan      html  css  js  c++  java
  • 【做题笔记】洛谷P4513小白逛公园

    很水的紫题

    题目大意

    给您一个长度为 (n) 的序列,您需要写一个数据结构,支持以下操作

    1. 查询 ([l,r]) 的最大字段和;
    2. (a_p) 变成 (s)

    solution

    显然考虑线段树。

    对于操作二,更改 (a_p) 在线段树中会影响到的结点即可。

    单独考虑操作一。

    其实有一种 (mathcal{O}(n)) 的求最大子段和的做法,但本题需要一个 (mathcal{O}(logn)) 的做法。

    放个图:

    3WzF0J.png

    对于这样一个区间,她的最大子段和只有三种可能:完全在左区间,完全在右区间,或者是贯穿了左右区间。

    然后分别在左、右区间的最大字段和怎么维护待会讲

    贯穿了中线的那个其实也很好维护,只要记录左区间的后缀,右区间的前缀,然后贯穿中线的就是这两个加起来。

    所以这一整个区间的最大子段和就是上述三种可能的最大值。

    那么现在问题聚焦在了如何求左区间的最大子段和、右区间的最大子段和。

    以左区间为例,把左区间再次一分为二,现在要求左区间的最大子段和:

    3WzqgK.png

    这种情况也就是左区间的最大子段和

    3fSAKS.png

    这种情况也就是左区间和+右区间左半边最大子段和

    然后不断递归就能搞到答案了。

    右区间同理。

    说到底,仍然是递归的思想。

    有一些细节,见代码:

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #define DEBUG printf("This is OK
    ")
    
    using namespace std;
    
    int n,q,a[5000100];
    
    struct SegmentTree
    {
    	int l,r;
    	long long lmax,rmax,ans,sum;
    	#define l(x) t[x].l
    	#define r(x) t[x].r
    	#define lmax(x) t[x].lmax //lmax是左区间最大子段和
    	#define rmax(x) t[x].rmax //rmax是右区间最大子段和
    	#define ans(x) t[x].ans //ans是整个 [l,r] 的最大子段和
    	#define sum(x) t[x].sum
    };
    SegmentTree t[4*5000010];
    
    void merge(int p) //合并信息
    {
    	sum(p)=sum(p*2)+sum(p*2+1); //区间和
    	lmax(p)=max(lmax(p*2),lmax(p*2+1)+sum(p*2)); 
    	rmax(p)=max(rmax(p*2+1),rmax(p*2)+sum(p*2+1));
    	ans(p)=max(max(ans(p*2),ans(p*2+1)),lmax(p*2+1)+rmax(p*2));
        //以上合并信息同思路
    }
    
    void build(int p,int l,int r)
    {
    	l(p)=l,r(p)=r;
    	if(l==r) {sum(p)=lmax(p)=rmax(p)=ans(p)=a[l];return ;}
    	int mid=(l+r)>>1;
    	build(p*2,l,mid);
    	build(p*2+1,mid+1,r);
    	merge(p); //合并信息
    }
    
    void change(int p,int x,int v)
    {
    	if(l(p)==r(p))
    	{
    		sum(p)=lmax(p)=rmax(p)=ans(p)=v;
    		return ;
    	}
    	int mid=(l(p)+r(p))>>1;
    	if(x<=mid) change(p*2,x,v);
    	else change(p*2+1,x,v);
    	merge(p); //进行了更改,更新信息
    }
    
    SegmentTree ask(int p,int l,int r) //由于这里需要涉及多个结构体信息,所以直接把ask函数变成一个结构体函数
    {
    	if(l<=l(p)&&r>=r(p)) return t[p];
    	int mid=(l(p)+r(p))>>1;
    	if(r<=mid) return ask(p*2,l,r); //如果右端点都在左半段,那直接去查左半段就好了
    	else if (l>mid) return ask(p*2+1,l,r); //同上
    	else //既有右端点,又有左端点
    	{
    		SegmentTree x=ask(p*2,l,r),y=ask(p*2+1,l,r),node;
            //x,y是左、右区间的信息
    		node.sum=x.sum+y.sum;
    		node.lmax=max(x.lmax,x.sum+y.lmax);
    		node.rmax=max(y.rmax,y.sum+x.rmax);
    		node.ans=max(max(x.ans,y.ans),x.rmax+y.lmax); //和那个meger函数一样的思路。
    		return node;
    	}
    }
    
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9') s=s*10+ch-'0' , ch=getchar();
    	return s*w;
    }
    
    int main()
    {
    	n=read(),q=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	build(1,1,n);
    	//DEBUG;
    	while(q--)
    	{
    		int K;
    		K=read();
    		if(K==1)
    		{
    			int l,r;
    			l=read(),r=read();
    			if(l>r) swap(l,r);
    			SegmentTree ans=ask(1,l,r);
    			cout<<ans.ans<<"
    ";
    		}
    		else
    		{
    			int p,s;
    			p=read(),s=read();
    			change(1,p,s);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    [转]nmake命令(windows下的makefile)
    [转]Visual Studio 2010 C++ 工程文件解读
    [转]开源库的编译
    强软弱虚引用试验
    ArtHas JVM在线排查工具
    JVM常用参数
    CMS两个常见问题
    jvisual vm连接
    jconsole连接
    JVM调优
  • 原文地址:https://www.cnblogs.com/BlueInRed/p/12404486.html
Copyright © 2011-2022 走看看