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] [x1∼y1]之间,且右端点在 [ x 2 ∼ y 2 ] [x_2\sim y_2] [x2∼y2]之间的最大子段和,数据保证 x 1 ≤ x 2 , y 1 ≤ y 2 x_1\leq x_2,y_1\leq y_2 x1≤x2,y1≤y2,但是不保证端点所在的区间不重合,同样也不需要修改
分析
除了维护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;
}