AT2412 【最大の和】
这是什么!!
无修改 线段树 板子啊!!
看到题解中竟没有一片线段树题解,于是我打算来普及一发装一波
这道题仅涉及两个线段树最基本操作:
建树 以及 区间查询
代码注释很详细,这里就不过多赘述,直接上代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define maxn 100005
using namespace std;
//优雅的空行
int n,k,a[maxn<<2],L[maxn<<2],R[maxn<<2],sum[maxn<<2];
//L[rt]节点rt的左区间,R[rt]节点rt的右区间,sum[rt]维护区间rt的和
//一些人习惯写到结构体里,但还是习惯写数组
void build(int rt,int l,int r)//建l~r区间的线段树,当前编号为rt
{
L[rt]=l,R[rt]=r;//记录rt区间的左端点与右端点
if(l==r){//当区间只剩一时个点,进行赋值操作
sum[rt]=a[l];//写成sum[rt]=a[r]等效
return ;//然后向上回溯
}
int mid=(l+r)>>1;//注意:线段树是一颗二叉树,因此要取中点
build(rt<<1,l,mid);//递归建造rt的左子树(左儿子),rt<<1 -> rt*2
build(rt<<1|1,mid+1,r);//递归建造rt的右子树(右儿子),rt<<1|1 -> rt*2+1
sum[rt]=sum[rt<<1]+sum[rt<<1|1];//向上回溯时更新sum[rt]的值
}
int query(int rt,int l,int r)//区间查询:查询l~r区间的和
{
if(L[rt]==l&&R[rt]==r)return sum[rt];//当当前区间与所查询区间重合时,直接返回sum[rt]
int mid=(L[rt]+R[rt])>>1;//同样二分,取中点
if(r<=mid)//如果要查询的区间完全在左子树,则进入左子树
return query(rt<<1,l,r);
if(l>mid)//如果要查询的区间完全在右子树,则进入右子树
return query(rt<<1|1,l,r);
return
query(rt<<1,l,mid)+query(rt<<1|1,mid+1,r);//否则返回其在左区间与右区间的和
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);//调用入口 :从节点1开始建造1~n的线段树
int maxm=-9999;
for(int i=1;i<=n;i++){
int j=i+k-1;//保证区间长度为k
if(j>n)break;
maxm=max(maxm,query(1,i,j));//从节点1开始向下,查询i~j的和,同时用maxm记录答案
}
cout<<maxm<<'\n';
return 2333;//卖一波萌
}
有一点不好的事:
线段树的常数一般很大,有些题会卡这一点
此题正解应该为前缀和,大概就快一个log吧..
啊哈哈
为了代码整洁,多加了几个空行..