次元传送门,扶稳坐好
题意:
给定一个长度为n的序列,可能存在负数
之后是m次询问,每次给出x和y
请你在[x..y]中找到一个子区间使其区间和最大
分析:
线段树练习题,懂?
一开始用自己的方法写了一棵枝繁叶茂的树,改了改就TLE了
后来借鉴别人的模板,才算是学会了这种写法
貌似实现起来复杂度差别还是挺大的,虽然不明觉厉
代码实现(正解)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Max(i,j) i>j?i:j
//据说这样能快点,比algorithm库中的函数更牛逼一点,至少看起来确实如此
#define MAX(i,j,k) Max(i,max(j,k))
//由于Sec值得传递会对三个量取MAX,所以这样写一句会方便很多
const int N=500002;
//看到留言中有人说N给的范围不对,也没多想,反正大点不会MLE,看看内存限制就知道了
struct Node{
int Left,Right,Sum,Pre,Suf,Sec;
//分别存储左边界,右边界,区间和,最大前缀,最大后缀,最大子区间和
}Tree[N*4];
int V[N];//原数组,用来直接存储Input数据并建树
void Build(int s,int t,int n){//植树函数,s为区间左界,t为区间右界,n为当前结点下标
int mid=(s+t)>>1;//位运算优化,求区间中界
Tree[n].Left=s;
Tree[n].Right=t;//对当前结点的左右界赋值
if(s==t){//如果当前结点为叶子结点
Tree[n].Sec=Tree[n].Sum=Tree[n].Pre=Tree[n].Suf=V[s];
//那么各项值都为序列中的第n个数,没商量
return;
}
//进入下面就代表当前结点不是叶子结点
Build(s,mid,2*n);//对左儿子建树
Build(mid+1,t,2*n+1);//对有儿子建树
//左儿子结点定位2n,右儿子为2n+1
Tree[n].Sum=Tree[2*n].Sum+Tree[2*n+1].Sum;//Sum由两个儿子相加合成
Tree[n].Pre=Max(Tree[2*n].Pre,Tree[2*n].Sum+Tree[2*n+1].Pre);//Pre只可能是左儿子的最大前缀或左儿子所有+右儿子最大前缀,取个MAX就好
Tree[n].Suf=Max(Tree[2*n+1].Suf,Tree[2*n].Suf+Tree[2*n+1].Sum);
Tree[n].Sec=MAX(Tree[2*n].Sec,Tree[2*n+1].Sec,Tree[2*n].Suf+Tree[2*n+1].Pre);//最大子区间和可以用分治的思想考虑,只能是左儿子的最大子区间和、右儿子的最大子区间和、左儿子的最大后缀+右儿子的最大前缀这三者中的一者,取个MAX就好
}
Node GetAns(int s,int t,int p){//用来得到某个区间的性质,这个函数会将其Sec、Pre、Suf打包返回
if(Tree[p].Left==s&&Tree[p].Right==t)
return Tree[p];
int mid=(Tree[p].Left+Tree[p].Right)>>1;
if(mid>=t)
return GetAns(s,t,2*p);
if(mid<s)
return GetAns(s,t,2*p+1);
Node LeftAns=GetAns(s,mid,2*p);
Node RightAns=GetAns(mid+1,t,2*p+1);
Node Ans;
Ans.Sec=MAX(LeftAns.Sec,RightAns.Sec,LeftAns.Suf+RightAns.Pre);
Ans.Pre=Max(LeftAns.Pre,LeftAns.Sum+RightAns.Pre);
Ans.Suf=Max(RightAns.Suf,LeftAns.Suf+RightAns.Sum);
return Ans;
}//其转移方程类比植树函数中的就好,注意Mid和植树中的Mid写法不太一样哦,因为要保证他们一样
int main(void){//主函数不解释
int n,m,x,y;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",V+i);
Build(1,n,1);
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&x,&y),printf("%d\n",GetAns(x,y,1).Sec);
return 0;
}//By YOUSIKI
代码实现(TLE版本)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX(i,j,k) max(i,max(j,k))
#define INF 0x3f3f3f3f
const int N=500005;
struct Node{
int Left,Right,Sum,MaxElement,MaxPrefix,MaxSuffix,MaxSection;
Node *LeftSon,*RightSon;
Node(){
Left=Right=Sum=MaxElement=MaxPrefix=MaxSuffix=MaxSection=0;
LeftSon=RightSon=NULL;
}
void Clear(void){
if(LeftSon!=NULL)
LeftSon->Clear();
if(RightSon!=NULL)
RightSon->Clear();
Left=Right=Sum=MaxElement=MaxPrefix=MaxSuffix=MaxSection=0;
LeftSon=RightSon=NULL;
}
void BuildTree(const int Vector[],const int L,const int R){
if(L>=R) return;
Left=L,Right=R;
if(L==R-1){
Sum=Vector[L];
MaxElement=Sum;
MaxPrefix=max(MaxPrefix,Sum);
MaxSuffix=max(MaxSuffix,Sum);
MaxSection=max(MaxSection,Sum);
}
else{
const int Mid=(L+R)>>1;
LeftSon=new Node,RightSon=new Node;
LeftSon->BuildTree(Vector,L,min(Mid,R));
RightSon->BuildTree(Vector,max(Mid,L),R);
Sum=LeftSon->Sum+RightSon->Sum;
MaxElement=max(LeftSon->MaxElement,RightSon->MaxElement);
MaxPrefix=max(LeftSon->MaxPrefix,LeftSon->Sum+RightSon->MaxPrefix);
MaxSuffix=max(RightSon->MaxSuffix,RightSon->Sum+LeftSon->MaxSuffix);
MaxSection=MAX(LeftSon->MaxSection,RightSon->MaxSection,LeftSon->MaxSuffix+RightSon->MaxPrefix);
}
}
int GetMaxPrefix(const int L,const int R){
if(L>=R) return 0;
if(L==Left&&R==Right)
return MaxPrefix;
const int Mid=(Left+Right)>>1;
return max(LeftSon->GetMaxPrefix(L,min(R,Mid)),LeftSon->GetSum(L,min(Mid,R))+RightSon->GetMaxPrefix(max(Mid,L),R));
}
int GetMaxSuffix(const int L,const int R){
if(L>=R) return 0;
if(L==Left&&R==Right)
return MaxSuffix;
const int Mid=(Left+Right)>>1;
return max(RightSon->GetMaxSuffix(max(Mid,L),R),RightSon->GetSum(max(Mid,L),R)+LeftSon->GetMaxSuffix(L,min(Mid,R)));
}
int GetMaxSection(const int L,const int R){
if(L>=R) return 0;
if(L==Left&&R==Right)
return MaxSection;
const int Mid=(Left+Right)>>1;
return MAX(LeftSon->GetMaxSection(L,min(Mid,R)),RightSon->GetMaxSection(max(Mid,L),R),LeftSon->GetMaxSuffix(L,min(Mid,R))+RightSon->GetMaxPrefix(max(Mid,L),R));
}
int GetSum(const int L,const int R){
if(L>=R) return 0;
if(L==Left&&R==Right)
return Sum;
const int Mid=(Left+Right)>>1;
return LeftSon->GetSum(L,min(Mid,R))+RightSon->GetSum(max(Mid,L),R);
}
int GetMaxElement(const int L,const int R){
if(L>=R) return -INF;
if(L==Left&&R==Right)
return MaxElement;
const int Mid=(Left+Right)>>1;
return max(LeftSon->GetMaxElement(L,min(Mid,R)),RightSon->GetMaxElement(max(Mid,L),R));
}
}Root;
int V[N],n,m;
int main(){//SPOJ GSS1 (TLE)
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&V[i]);
Root.BuildTree(V,1,n+1);
scanf("%d",&m);
for(int i=0,x,y;i<m;i++){
scanf("%d%d",&x,&y);
int ans=Root.GetMaxSection(x,y+1);
printf("%d\n",ans?ans:Root.GetMaxElement(x,y+1));
}
return 0;
}//By YOUSIKI
注意:
前者中区间下标是左闭右闭的,而后者是左闭右开的
By YOUSIKI