心得
昨天杭电不会线段树维护最大子段和,补了一下,发现和维护最长01的区间合并是一样的
顺便网上搜了一下子段和的SPOJ的GSS1到GSS8,觉得还挺好的,决定补一下
其中GSS1、GSS2(坑题待补)、GSS3、GSS5是最大子段和,GSS4是区间开根(之前做过就不管了)
GSS6、GSS7、GSS8两个是平衡树(treap/splay),一个是树剖,对于目前来说,太难待补
SPOJ - GSS1 Can you answer these queries I(裸题)
You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ).
A query is defined as follows:
Query(x,y) = Max { a[i]+a[i+1]+...+a[j] ; x ≤ i ≤ j ≤ y }.
Given M queries, your program must output the results of these queries.
int范围,非空序列,没有修改,每次问区间[l,r]最大子段和,模板题
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,a[N],l,r;
struct node
{
int lmx,rmx,mx,sum;
}e[N*4];
void pushup(node &fa,node &lson,node &rson)
{
fa.sum=lson.sum+rson.sum;
fa.lmx=max(lson.lmx,lson.sum+rson.lmx);
fa.rmx=max(rson.rmx,rson.sum+lson.rmx);
fa.mx=max(max(lson.mx,rson.mx),lson.rmx+rson.lmx);
}
void build(int p,int l,int r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=0;
if(l==r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=a[l];
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(e[p],e[p<<1],e[p<<1|1]);
}
void update(int p,int l,int r,int x,int v)
{
if(l==r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=v;
return;
}
int mid=(l+r)/2;
if(x<=mid)update(p<<1,l,mid,x,v);
else update(p<<1|1,mid+1,r,x,v);
pushup(e[p],e[p<<1],e[p<<1|1]);
}
node ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return e[p];
int mid=(l+r)/2;
if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
node ls=ask(p<<1,l,mid,ql,qr),rs=ask(p<<1|1,mid+1,r,ql,qr),ans;
pushup(ans,ls,rs);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&l,&r);
printf("%d\n",ask(1,1,n,l,r).mx);
}
return 0;
}
SPOJ - GSS3 Can you answer these queries III(裸题)
You are given a sequence A of N (N <= 50000) integers between -10000 and 10000.
On this sequence you have to apply M (M <= 50000) operations:
modify the i-th element in the sequence
for given x y print max{Ai + Ai+1 + .. + Aj | x<=i<=j<=y }.
int范围,单点修改,每次询问[l,r]非空序列子段和(即负的仍输出负的)
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,a[N],op,x,y;
struct node
{
int lmx,rmx,mx,sum;
}e[N*4];
void pushup(node &fa,node &lson,node &rson)
{
fa.sum=lson.sum+rson.sum;
fa.lmx=max(lson.lmx,lson.sum+rson.lmx);
fa.rmx=max(rson.rmx,rson.sum+lson.rmx);
fa.mx=max(max(lson.mx,rson.mx),lson.rmx+rson.lmx);
}
void build(int p,int l,int r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=0;
if(l==r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=a[l];
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(e[p],e[p<<1],e[p<<1|1]);
}
void update(int p,int l,int r,int x,int v)
{
if(l==r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=v;
return;
}
int mid=(l+r)/2;
if(x<=mid)update(p<<1,l,mid,x,v);
else update(p<<1|1,mid+1,r,x,v);
pushup(e[p],e[p<<1],e[p<<1|1]);
}
node ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return e[p];
int mid=(l+r)/2;
if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
node ls=ask(p<<1,l,mid,ql,qr),rs=ask(p<<1|1,mid+1,r,ql,qr),ans;
pushup(ans,ls,rs);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&op,&x,&y);
if(op==0)update(1,1,n,x,y);
else printf("%d\n",ask(1,1,n,x,y).mx);
}
return 0;
}
SPOJ - GSS5 Can you answer these queries V(分类讨论)
You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| <= 10000 , 1 <= N <= 10000 ).
A query is defined as follows:
Query(x1,y1,x2,y2) = Max { A[i]+A[i+1]+...+A[j] ; x1 <= i <= y1 , x2 <= j <= y2 and x1 <= x2 , y1 <= y2 }.
Given M queries (1 <= M <= 10000), your program must output the results of these queries.
给定x1,y1,左端点L在[x1,y1]间任取,给定x2,y2,右端点R在[x2,y2]间任取,
保证x1<=x2,y1<=y2,但有区间重合和区间包含覆盖的情况,问[L,R]的最大子段和
第一种情况,两个区间无交集,
[x1,y1][x2,y2]即y1<x2,两段分离,即问左段的最大后缀+中间段的和+右段的最大前缀
第二种情况,两个区间有相交或包含的情况,
[x1, 【x2 y1] , y2】 总体来说,都是这种情况,[x2,y1]是交集部分
①答案完全在[x2,y1]中,是[x2,y1]的区间最大值
②左端点向外扩展,即左端点在[x1,x2]中,右端点在[x2,y2]中,
询问[x1,x2]最大后缀+[x2,y2]最大前缀,注意减掉公共部分x2单点值
③右端点向外扩展,即右端点在[y1,y2]中,左端点在[x1,y1]中,
询问[x1,y1]最大后缀+[y1,y2]最大前缀,注意减掉公共部分y1单点值
④如果两个点都向外扩展,会在②③中各被算一次,因此,无需拎出来单独讨论
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int t,n,m,a[N],l1,l2,r1,r2;
struct node
{
int lmx,rmx,mx,sum;
}e[N*4];
void pushup(node &fa,node &lson,node &rson)
{
fa.sum=lson.sum+rson.sum;
fa.lmx=max(lson.lmx,lson.sum+rson.lmx);
fa.rmx=max(rson.rmx,rson.sum+lson.rmx);
fa.mx=max(max(lson.mx,rson.mx),lson.rmx+rson.lmx);
}
void build(int p,int l,int r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=0;
if(l==r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=a[l];
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(e[p],e[p<<1],e[p<<1|1]);
}
void update(int p,int l,int r,int x,int v)
{
if(l==r)
{
e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=v;
return;
}
int mid=(l+r)/2;
if(x<=mid)update(p<<1,l,mid,x,v);
else update(p<<1|1,mid+1,r,x,v);
pushup(e[p],e[p<<1],e[p<<1|1]);
}
node ask(int p,int l,int r,int ql,int qr)
{
if(ql>qr)return node{0,0,0,0};
if(ql<=l&&r<=qr)return e[p];
int mid=(l+r)/2;
if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
node ls=ask(p<<1,l,mid,ql,qr),rs=ask(p<<1|1,mid+1,r,ql,qr),ans;
pushup(ans,ls,rs);
return ans;
}
int query(int l1,int r1,int l2,int r2)
{
//[l1, r1] [l2, r2]的分离
if(r1<l2)
{
int suf=ask(1,1,n,l1,r1).rmx;
int mid=ask(1,1,n,r1+1,l2-1).sum;//ask中写入ql>qr的情形
int pre=ask(1,1,n,l2,r2).lmx;
return suf+mid+pre;
}
//[l1 ,(l2 , r1] r2)的嵌套与包含
int ans=ask(1,1,n,l2,r1).mx;//完全包含在[l2,r1]内
if(l1<l2)ans=max(ans,ask(1,1,n,l1,l2).rmx+ask(1,1,n,l2,r1).lmx-a[l2]);//l2端向左出头 l2端被计入两次 应减一次 注意如有修改a[]配套修改
if(r1<r2)ans=max(ans,ask(1,1,n,l1,r1).rmx+ask(1,1,n,r1,r2).lmx-a[r1]);//r1端向右出头 r1端被计入两次
return ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
printf("%d\n",query(l1,r1,l2,r2));
}
}
return 0;
}