洛谷P4513 小白逛公园 题解

题目链接:点这里
题目描述

在小新家附近有一条“公园路”,路的一边从南到北依次排着 n 个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。

一开始,小白就根据公园的风景给每个公园打了分。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第 a 个和第 b 个公园之间(包括 a,b 两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。

那么,就请你来帮小白选择公园吧。
输入格式

第一行,两个整数 n 和 m,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。

接下来 n 行,每行一个整数,依次给出小白开始时对公园的打分。

接下来 m 行,每行三个整数。其中第一个整数 k 为 1 或 2。

k=1 表示,小新要带小白出去玩,接下来的两个整数 a 和 b 给出了选择公园的范围 (1≤a,b≤n)。测试数据可能会出现 a>b 的情况,需要进行交换;
k=2 表示,小白改变了对某个公园的打分,接下来的两个整数 p 和 s,表示小白对第 p 个公园的打分变成了 s(1≤p≤N)。

输出格式

小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。
一句话题意
维护一个数组a,满足以下两个操作
一、更改位于p位置的数值为s
二、求出给出的区间[l,r]中a的最大子段和
一句话思路
直接上线段树没什么好说的了吧
思路
除了node和mergenode()其他都和普通的线段树板子一样
接下来说怎么写node和mergenode()
node:存四个变量:
1: sum:本段总长度
2: lmax:包括区间最左端的点时最大值
3: rmax:包括区间最右端的点时最大值
4: maxx:本段最大子段和
mergenode(node a,node b)
其中需要维护node中的四个变量
1: sum:没什么好说的直接a.sum+b.sum就完了
2: lmax:取a.lmax和a.sum+b.lmax中最大的那个
3: rmax:取b.rmax和a.rmax+b.sum中最大的那个
4: maxx:取a.maxx,b.maxx和a.rmax+b.lmax中最大的那个
显而易见这个是对的

代码:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x = 0, f = 1;char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}
	return x * f;
}
int n, m;
const int maxn = 5e5 + 10, maxm = 1e5 + 10;
class Segment_tree{
	private:
struct node{
	int lmax,rmax,maxx,sum;
}d[maxn * 4];
node mergenode(node a,node b){
	node tmp;
	tmp.lmax = max(a.lmax,a.sum + b.lmax);
	tmp.rmax = max(b.rmax,b.sum + a.rmax);
	tmp.maxx = max(max(a.maxx,b.maxx),a.rmax + b.lmax);
	tmp.sum = a.sum + b.sum;
	return tmp;
}
void build(int l,int r,int p){
	if(l == r){d[p].lmax = d[p].rmax = d[p].maxx = d[p].sum = a[l];return;}
	int mid = (l + r) >> 1;build(l,mid,p << 1);build(mid + 1,r,(p << 1) | 1);
	d[p] = mergenode(d[p << 1],d[(p << 1) | 1]);
}
void update(int x,int s,int t,int p,int change){
	if(s == t && s == x){d[p] = {change,change,change, change};return;}
	int mid = (s + t) >> 1;
	if(mid >= x)update(x,s,mid,p << 1,change);
	else update(x,mid + 1,t,(p << 1) | 1,change);
	d[p] = mergenode(d[p << 1],d[(p << 1) | 1]);
}
node query(int l,int r,int s,int t,int p){
	if(l <= s && t <= r)return d[p];
	int mid = (s + t) >> 1;
	if(mid >= r)return query(l,r,s,mid,p << 1);
	else if(mid < l)return query(l,r,mid + 1,t,(p << 1) | 1);
	else return mergenode(query(l,r,s,mid,p << 1),query(l,r,mid + 1,t,(p << 1) | 1));
}
public:
int a[maxn];
void build(int n){build(1,n,1);}
void update(int pos,int change){update(pos,1,n,1,change);}
int query(int x,int y){if(x > y)swap(x,y);return query(x,y,1,n,1).maxx;}
}tree;
signed main(){
	n = read(); m = read();
	for(int i = 1;i <= n;i++){tree.a[i] = read();}
	tree.build(n); int op, x, y;
	for(int i = 1;i <= m;i++){
		op = read(); x = read(); y = read();
		if(op == 1)printf("%d\n",tree.query(x,y));
		else tree.update(x,y);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值