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吧..
啊哈哈

为了代码整洁,多加了几个空行..

posted @ 2019-10-25 15:02  Hydrogen_Helium  阅读(228)  评论(0编辑  收藏  举报