线段树目的:使算法时间复杂度变为O(logn)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=50010;
int T,n,j,i,k;
int a[maxn];
char s[10];
struct tree{
int l,r,sum;
}node[maxn<<2]; //maxn乘以4
void pushup(int i){
node[i].sum=node[i<<1].sum+node[i<<1|1].sum; //该节点的数值为下方两个节点之和
}
void build(int l,int r,int i) //i代表根节点,建立线段树
{
node[i]={l,r}; //初始化结构体
if(l==r) //出口条件
{
node[i].sum=a[l];
return;
}
int mid=l+r>>1;
build(l,mid,i<<1); //递归,左侧节点
build(mid+1,r,i<<1|1); //递归,右侧节点,指i*2+1
pushup(i); //更新该节点数值
}
void update(int p,int w,int i) //更改数值,指把第p个数值加上w
{
if(node[i].l==p&&node[i].r==p) //node[i].l指第i个节点的左节点
{
node[i].sum+=w;
return;
}
int mid=node[i].l+node[i].r>>1;
if(p<=mid) update(p,w,i<<1);
else update(p,w,i<<1|1);
pushup(i);
}
int query(int l,int r,int i)
{
if(l<=node[i].l&&r>=node[i].r)
{
return node[i].sum;
}
int mid=node[i].l+node[i].r>>1;
int res=0;
if(l<=mid) res+=query(l,r,i<<1);
if(r>mid) res+=query(l,r,i<<1|1);
return res;
}
int main()
{
scanf("%d",&T);
while(T--)
{
printf("Case %d:\n",++k);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
while(scanf("%s",s)!=EOF)
{
if(!strcmp(s,"End")) break;
else
{
int x,y;
scanf("%d%d",&x,&y);
if(!strcmp(s,"Query"))
printf("%d\n",query(x,y,1));
if(!strcmp(s,"Add"))
{
update(x,y,1);
}
if(!strcmp(s,"Sub"))
{
update(x,-y,1);
}
}
}
}
}
注意:开线段树空间时一般要开为数组大小的四倍,即maxn<<2
否则会造成time limit。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50010;
struct tree{
int l,r,lmax,rmax,sum,ans;
//lmax指该区间从左端点开始的最子线段和
//rmax指该区间从右端点开始的最子大线段和
//sum指该区间的和
//ans指该区间的最大字段和
}node[N<<2];
int a[N];
int m,n;
void pushup(int i) //更新各个点的数据
{
node[i].sum=node[i<<1].sum+node[i<<1|1].sum;
node[i].lmax=max(node[i<<1].lmax,node[i<<1|1].lmax+node[i<<1].sum);
//该区间左端点起的最大字段和为左区间lamx左区间sum和右区间lmax之和的较大值
node[i].rmax=max(node[i<<1|1].rmax,node[i<<1].rmax+node[i<<1|1].sum);
//该区间右端点起的最大字段和为右区间ramx右区间sum和左区间rmax之和的较大值
node[i].ans=max(max(node[i<<1].ans,node[i<<1|1].ans),node[i<<1].rmax+node[i<<1|1].lmax);
//该区间最大子段和分为最大子段完全在左区间,完全在右区间和横跨中点三种情况
}
void build(int l,int r,int i) //建树
{
node[i]={l,r};
if(l==r)
{
node[i]={l,r,a[l],a[l],a[l],a[l]}; //初始化线段树中的各点
return;
}
int mid=l+r >> 1;
build(l,mid,i<<1);
build(mid+1,r,i<<1|1);
pushup(i);
}
tree merge(tree l,tree r) //合并
{
tree res;
res.sum=l.sum+r.sum;
res.lmax=max(l.lmax,l.sum+r.lmax);
res.rmax=max(r.rmax,r.sum+l.rmax);
res.ans=max(max(l.ans,r.ans),l.rmax+r.lmax);
return res;
}
tree query(int l,int r,int i)
{
if(l<=node[i].l&&r>=node[i].r) return node[i];
int mid= node[i].l+node[i].r >> 1;
if(r<=mid)
return query(l,r,i<<1); //完全包含于左边时
else if(l>mid)
return query(l,r,i<<1|1); //完全包含于右边时
else //当l和r横跨两端时
{
tree L=query(l,r,i<<1); //将左边结果存储在L线段树中
tree R=query(1,r,i<<1|1); //将右边结果存储在R线段树中
return merge(L,R); //返回左右合并后的线段树
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,n,1);
cin>>m;
while(m--)
{
int l,r;
cin>>l>>r;
printf("%d\n",query(l,r,1).ans);
}
}