#线段树,猫树#SP1043 GSS1 SP1716 GSS3 SP2916 GSS5

GSS3

题目

多次询问区间最大子段和,带单点修改


分析

用线段树维护区间最大前缀和、区间最大后缀和,区间最大子段和,因为单点修改,所以不难(我也不会区间修改)


代码

#include <cstdio>
struct node{int sum,lmax,rmax,w;}tree[200001];
int n,m,a[50001];
int in(){
    int f=1,ans=0; char c=getchar();
    while ((c<48||c>57)&&c!='-') c=getchar();
    if (c=='-') f=-f,c=getchar();
    while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
    return ans*f;
}
int max(int a,int b){return (a>b)?a:b;}
void calc(int k){
    tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
    tree[k].lmax=max(tree[k<<1].lmax,tree[k<<1].sum+tree[k<<1|1].lmax);
    tree[k].rmax=max(tree[k<<1|1].rmax,tree[k<<1|1].sum+tree[k<<1].rmax);
    tree[k].w=max(max(tree[k<<1].w,tree[k<<1|1].w),tree[k<<1].rmax+tree[k<<1|1].lmax);
}
void build(int k,int l,int r){
    if (l==r) {tree[k]=(node){a[l],a[l],a[l],a[l]}; return;}
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    calc(k);
}
node ask(int k,int l,int r,int x,int y){
    if (l==x&&r==y) return tree[k];
    int mid=(l+r)>>1;
    if (y<=mid) return ask(k<<1,l,mid,x,y);
    else if (x>mid) return ask(k<<1|1,mid+1,r,x,y);
    else{
        node a=ask(k<<1,l,mid,x,mid);
        node b=ask(k<<1|1,mid+1,r,mid+1,y);
        node c;
        c.sum=a.sum+b.sum;
        c.lmax=max(a.lmax,a.sum+b.lmax);
        c.rmax=max(b.rmax,b.sum+a.rmax);
        c.w=max(max(a.w,b.w),a.rmax+b.lmax);
        return c;
    }
}
void change(int k,int l,int r,int x,int y){
    if (l==r) {tree[k]=(node){y,y,y,y}; return;}
    int mid=(l+r)>>1;
    if (x<=mid) change(k<<1,l,mid,x,y); else change(k<<1|1,mid+1,r,x,y);
    calc(k); 
}
int main(){
    n=in(); 
    for (register int i=1;i<=n;i++) a[i]=in();
    build(1,1,n); m=in();
    while (m--){
        int q=in(); int x=in(); int y=in();
        if (q==1){
            if (x>y) x^=y,y^=x,x^=y;
            printf("%d\n",ask(1,1,n,x,y).w);
        }
        else change(1,1,n,x,y);
    }
    return 0;
}

GSS1

题目

多次询问区间最大子段和,不带修改


分析1

首先是线段树版本的,已经出现过了,不多讲


分析2

那有没有一种 O ( n l o g n ) O(nlogn) O(nlogn)预处理, O ( 1 ) O(1) O(1)输出的方法呢,实际上是有的,考虑 R M Q RMQ RMQ,这就是一种优秀的算法,那有没有一种媲美 R M Q RMQ RMQ,比 R M Q RMQ RMQ支持更多操作的呢,其实是有的,虽然也不支持修改,详见代码


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=70011;
int p[21][N],w[21][N],a[N],n,len,pos[N],lg[N<<1];
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline void print(int ans){
	if (ans<0) putchar('-'),ans=-ans;
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed max(int a,int b){return a>b?a:b;}
inline void build(int dep,int k,int l,int r){
	if (l==r) {pos[l]=k; return;}
	rr int sum,swm,mid=(l+r)>>1;
	sum=w[dep][mid]=p[dep][mid]=a[mid],swm=max(a[mid],0);
	for (rr int i=mid-1;i>=l;--i){
		sum+=a[i],swm+=a[i],
		p[dep][i]=max(p[dep][i+1],sum),//最大前后缀和
		w[dep][i]=max(w[dep][i+1],swm),//最大子段和
		swm=max(swm,0);
	}
	sum=w[dep][mid+1]=p[dep][mid+1]=a[mid+1],swm=max(a[mid+1],0);
	for (rr int i=mid+2;i<=r;++i){
		sum+=a[i],swm+=a[i],
		p[dep][i]=max(p[dep][i-1],sum),//最大前后缀和
		w[dep][i]=max(w[dep][i-1],swm),//最大子段和
		swm=max(swm,0);
	}
	build(dep+1,k<<1,l,mid),build(dep+1,k<<1|1,mid+1,r); 
}
inline signed query(int l,int r){
	if (l==r) return a[l];
	rr int z=lg[pos[l]]-lg[pos[l]^pos[r]];//这表示l和r的区间的位置,类比于zkw线段树
	return max(max(w[z][l],w[z][r]),p[z][l]+p[z][r]);
}
signed main(){
	for (len=2,n=iut();len<n;len<<=1);
	for (rr int i=1;i<=n;++i) a[i]=iut();
	for (rr int l=len*2,i=2;i<=l;++i) lg[i]=lg[i>>1]+1;
	build(1,1,1,len);
	for (rr int m=iut(),l,r;m;--m)
	    l=iut(),r=iut(),print(query(l,r)),putchar(10);
	return 0;
}

GSS5

题目

给定一个序列。查询左端点在 [ x 1 ∼ y 1 ] [x_1\sim y_1] [x1y1]之间,且右端点在 [ x 2 ∼ y 2 ] [x_2\sim y_2] [x2y2]之间的最大子段和,数据保证 x 1 ≤ x 2 , y 1 ≤ y 2 x_1\leq x_2,y_1\leq y_2 x1x2,y1y2,但是不保证端点所在的区间不重合,同样也不需要修改


分析

除了维护GSS1所需要的,还需要维护和,由于区间可能重合,所以需要分类讨论


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=20011;
int p[16][N],w[16][N],s[16][N],h[16][N],a[N],n,len,pos[N],lg[N<<1];
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline void print(int ans){
	if (ans<0) putchar('-'),ans=-ans;
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed max(int a,int b){return a>b?a:b;}
inline void build(int dep,int k,int l,int r){
	if (l==r) {pos[l]=k; return;}
	rr int sum,swm,mid=(l+r)>>1;
	sum=w[dep][mid]=p[dep][mid]=s[dep][mid]=h[dep][mid]=a[mid],swm=max(a[mid],0);
	for (rr int i=mid-1;i>=l;--i){
		sum+=a[i],swm+=a[i],s[dep][i]=sum,//和
		p[dep][i]=max(p[dep][i+1],sum),
		w[dep][i]=max(w[dep][i+1],swm),
		h[dep][i]=swm,swm=max(swm,0);//非负和
	}
	sum=w[dep][mid+1]=p[dep][mid+1]=s[dep][mid+1]=h[dep][mid+1]=a[mid+1],swm=max(a[mid+1],0);
	for (rr int i=mid+2;i<=r;++i){
		sum+=a[i],swm+=a[i],s[dep][i]=sum,
		p[dep][i]=max(p[dep][i-1],sum),
		w[dep][i]=max(w[dep][i-1],swm),
		h[dep][i]=swm,swm=max(swm,0);
	}
	build(dep+1,k<<1,l,mid),build(dep+1,k<<1|1,mid+1,r); 
}
inline signed query_sum(int l,int r){//区间和
	if (l>r) return 0;
	if (l==r) return a[l];
	rr int z=lg[pos[l]]-lg[pos[l]^pos[r]];
	return s[z][l]+s[z][r];
}
inline signed query_fro(int l,int r){//区间前半部分的子段和
	if (l>r) return 0;
	if (l==r) return a[l];
	rr int z=lg[pos[l]]-lg[pos[l]^pos[r]];
	return max(s[z][l]+p[z][r],h[z][l]);
}
inline signed query_beh(int l,int r){//区间后半部分的子段和
	if (l>r) return 0;
	if (l==r) return a[l];
	rr int z=lg[pos[l]]-lg[pos[l]^pos[r]];
	return max(s[z][r]+p[z][l],h[z][r]);	
}
inline signed query_amo(int l,int r){//区间中间的子段和
	if (l>r) return 0;
	if (l==r) return a[l];
	rr int z=lg[pos[l]]-lg[pos[l]^pos[r]];
	return max(max(w[z][l],w[z][r]),p[z][l]+p[z][r]);
}
inline signed query(int l1,int r1,int l2,int r2){
	rr int ans;
	if (r1<l2) return query_sum(r1+1,l2-1)+query_beh(l1,r1)+query_fro(l2,r2);//如果没有重合就直接分开了
	ans=query_amo(l2,r1);//中间子段和
	if (l1<l2) ans=max(ans,query_beh(l1,l2)+query_fro(l2,r2)-a[l2]);//左边子段和
	if (r1<r2) ans=max(ans,query_beh(l1,r1)+query_fro(r1,r2)-a[r1]);//右边子段和
	return ans; 
} 
signed main(){
	for (rr int i=2;i<=40000;++i) lg[i]=lg[i>>1]+1;
	for (rr int t=iut();t;--t){
	    for (len=2,n=iut();len<n;len<<=1);
	    for (rr int i=1;i<=n;++i) a[i]=iut();
	    build(1,1,1,len);
	    for (rr int m=iut(),l1,l2,r1,r2;m;--m)
	        l1=iut(),r1=iut(),l2=iut(),r2=iut(),print(query(l1,r1,l2,r2)),putchar(10);
    }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值