題解 SP1716 【GSS3 - Can you answer these queries III】


前言

首先說一下題意。就是每次給出 \(x\)\(y\) 兩個數,求 \(x\)\(y\) 這個區間的最大子段和

正文

分析

首先我們看這個數據范圍,我們顯然是要用線段樹來做這道題。

我們考慮如何pushup

pushup的操作

360截圖17001020107151123.png

區間最大字段和

我們考慮一個區間的最大子段

我們分 \(3\) 種情況討論

1. 有可能是左邊部分的最大子段和

asdsajdfhiujhkja.png

答案就是左邊部分的最大子段和

2. 也有可能右邊部分的最大子段和

asdasajdfhiujhkja.png

答案就是右邊部分的最大子段和

3. 最大最大和有可能跨越了中間

aasdasajdfhiujhkja.png

答案就是左邊部分右端點往左的最大子段和 \(+\) 左端點往右的最大子段和

發現

所以,我們需要對於所有節點,還要維護它們以左端點往右的最大子段和、右端點往左的最大子段和。

我們再考慮如何維護一個區間以左端點往右的最大子段和、右端點往左的最大子段和。

以左端點往右的最大子段和

我們先考慮以左端點往右的最大子段和

我們分 \(2\) 種情況討論

1. 不跨越中間

aaasajdfhiujhkja.png

答案就是左部分的以左端點往右的最大子段和

2. 跨越中間

asdssssajdfhiujhkja.png

答案就是左部分的和 \(+\) 右部分以左端點往右的最大子段和

以右端點往左的最大子段和

我們先考慮以右端點往左的最大子段和

我們分 \(2\) 種情況討論

1. 不跨越中間

kja.png

答案就是右部分的以左端點往右的最大子段和

2. 跨越中間

df.png

答案就是右部分的和 \(+\) 左部分以右端點往左的最大子段和

發現

我們發現我們還需要維護區間和,這個問題很簡單,不在講解了。

所以我們現在總共要維護 \(4\) 個東西

分別是

\(lans\)\(rans\)\(ans\)\(sum\)

邊界情況——即整個區間是一個點

我們可以發現

\(lans\)\(rans\)\(ans\)\(sum\) 都為這個點的值

代碼

Tree pushup(Tree L,Tree R){
	Tree z;
	z.sum=L.sum+R.sum;//和
	z.l=max(L.l,L.sum+R.l);//2種情況
	z.r=max(R.r,R.sum+L.r);//2種情況
	z.ans=max(max(L.ans,R.ans),L.r+R.l);//3種情況
	return z;
}

這里我寫了一個帶返回值的函數,就是為了下面方便啦。

查詢

上文已經講解了最大子段和的 \(3\) 中情況,已經知道最大子段和跟 \(4\) 個東西有關。

所以我們要定義一個返回值為 \(Tree\) 的函數。

那么現在,關鍵就在於合並區間,那么現在之前的pushup就可以被調用了。

Tree query(int x,int y,int num,int l,int r){
	if(l<=x&&r<=y)return t[num];
	int mid=(l+r)>>1;
	if(y<=mid)return query(x,y,ls,l,mid);//右區間和查詢區間沒有交,答案當然在左區間
	if(mid<x)return query(x,y,rs,mid+1,r);//左區間和查詢區間沒有交,答案當然在右區間
	return pushup(query(x,mid,ls,l,mid),query(mid+1,y,rs,mid+1,r));//是不是很簡潔?
}

代碼

我們已經把這道題的重點都搞清楚了,接下來就可以放代碼了,至於單點修改不會的請自行去學習

#include <bits/stdc++.h>
#define ls num<<1
#define rs num<<1|1
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>void write(T x){
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
template<typename T>void writen(T x){
	write(x);
	puts("");
}
const int MAXN=5e4+10;
struct Tree{
	int sum,l,r,ans;//維護的4個量
}t[MAXN*4];
int a[MAXN],f,x,y;
Tree pushup(Tree L,Tree R){
	Tree z;
	z.sum=L.sum+R.sum;//和
	z.l=max(L.l,L.sum+R.l);//2種情況
	z.r=max(R.r,R.sum+L.r);//2種情況
	z.ans=max(max(L.ans,R.ans),L.r+R.l);//3種情況
	return z;
}
void build(int l,int r,int num){//建樹
	if(l==r){
		t[num].sum=a[l];
		t[num].l=a[l];
		t[num].r=a[l];
		t[num].ans=a[l];//邊界初始化
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,ls);
	build(mid+1,r,rs);
	t[num]=pushup(t[ls],t[rs]);//pushup
}
void change(int l,int r,int num){//單點修改
	if(l==r){
		t[num].sum=y;
		t[num].l=y;
		t[num].r=y;
		t[num].ans=y;//邊界初始化
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)change(l,mid,ls);
	else change(mid+1,r,rs);
	t[num]=pushup(t[ls],t[rs]);
}
Tree query(int x,int y,int num,int l,int r){
	if(l<=x&&r<=y)return t[num];
	int mid=(l+r)>>1;
	if(y<=mid)return query(x,y,ls,l,mid);//右區間和查詢區間沒有交,答案當然在左區間
	if(mid<x)return query(x,y,rs,mid+1,r);//左區間和查詢區間沒有交,答案當然在右區間
	return pushup(query(x,mid,ls,l,mid),query(mid+1,y,rs,mid+1,r));//是不是很簡潔?
}
int main(){
	int n,T;
	read(n);
	for(int i=1;i<=n;i++)read(a[i]);
	build(1,n,1);
	read(T);
	while(T--){
		read(f);read(x);read(y);
		if(f==0)change(1,n,1);
		else writen(query(x,y,1,1,n).ans);
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM