1. 程式人生 > 其它 >線段樹的區間最值操作與區間歷史最值

線段樹的區間最值操作與區間歷史最值

技術標籤:資料結構與演算法學習

前提知識

基本的線段樹寫法:
洛谷線段樹1
洛谷線段樹2

以下是正文

一、區間最值操作

例題一、

在這裡插入圖片描述
Gorgeous Sequence

本題要求我們實現三個操作
在這裡插入圖片描述
對於第二種和第三種操作我們是已經知道如何寫了。

問題就在於第一種操作。

考慮第一種操作是取min
因此當我們這個區間內的最大值 
1.如果比給定的k來得小 那麼我們不需要任何操作
2.如果這個k<最大值 但是k > 第二大的值(以下稱為次大值)
那麼我們只需要將最大值全部變成k,修改一下區間和即可.
3.如果這個k<最大值 && k < 次大值 
那麼我們當前這個結點是處理不了的,直接push_down交給兒子結點

以下放出完整程式碼

#include <iostream>
#include <cstring>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
typedef long long ll;
const int N = 1100000;
struct Node{
    int L,R,tag;//這個tag是維護區間最值操作的
    ll sum;
    int fir_big,sec_big;
    int fir_num;
}tree[N*4];
int a[N];
inline
void push_up(int); void build_tree(int l,int r,int p){ //tag的取值是0~2^31-1 因此我們取-1 tree[p].L=l,tree[p].R=r,tree[p].tag=-1; if(l==r){ tree[p].sum=a[l]; tree[p].fir_big=a[l]; tree[p].sec_big=-1; tree[p].fir_num=1; return; } int mid = (l+r)>>
1; build_tree(l,mid,lc); build_tree(mid+1,r,rc); push_up(p); } inline void push_up(int p){ tree[p].sum=tree[lc].sum+tree[rc].sum; if(tree[lc].fir_big==tree[rc].fir_big){ tree[p].fir_big=tree[lc].fir_big; tree[p].fir_num=tree[lc].fir_num+tree[rc].fir_num; tree[p].sec_big=max(tree[lc].sec_big,tree[rc].sec_big); }else if(tree[lc].fir_big>tree[rc].fir_big){ tree[p].fir_big=tree[lc].fir_big; tree[p].fir_num=tree[lc].fir_num; tree[p].sec_big=max(tree[lc].sec_big,tree[rc].fir_big); }else{ tree[p].fir_big=tree[rc].fir_big; tree[p].fir_num=tree[rc].fir_num; tree[p].sec_big=max(tree[rc].sec_big,tree[lc].fir_big); } } inline void update(int p,int cnt_val){ if(cnt_val<tree[p].fir_big){ //最大值全部變成cnt_val tree[p].sum+=(1ll*cnt_val-tree[p].fir_big)*tree[p].fir_num; tree[p].fir_big=tree[p].tag=cnt_val; } } inline void push_down(int p){ if(~tree[p].tag){ update(lc,tree[p].tag); update(rc,tree[p].tag); tree[p].tag=-1; } } void modify(int p,int l,int r,int cnt_val){ if(tree[p].L>r||tree[p].R<l||tree[p].fir_big<=cnt_val) return; if(tree[p].L>=l&&tree[p].R<=r&&tree[p].sec_big<cnt_val){ update(p,cnt_val); return; } push_down(p); modify(lc,l,r,cnt_val); modify(rc,l,r,cnt_val); push_up(p); } int queryMax(int p,int l,int r){ if(tree[p].L>r||tree[p].R<l) return 0; if(tree[p].L>=l&&tree[p].R<=r){ return tree[p].fir_big; } push_down(p); return max(queryMax(lc,l,r),queryMax(rc,l,r)); } ll querySum(int p,int l,int r){ if(tree[p].L>r||tree[p].R<l) return 0; if(tree[p].L>=l&&tree[p].R<=r){ return tree[p].sum; } push_down(p); return querySum(lc,l,r)+querySum(rc,l,r); } #undef lc #undef rc int read(){ int x=0; char ch=0; while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar(); return x; } #if 0 這份程式碼完成三個功能 1.區間最小值操作 2.求區間最大值 3.求區間和 #endif // 0 int main() { int t,n,m; t=read(); while(t--){ n=read(); m=read(); for(int i=1;i<=n;i++){ a[i]=read(); } build_tree(1,n,1); for(int i=1;i<=m;i++){ int op,x,y,t; op=read(); if(op==0){ x=read(); y=read(); t=read(); modify(1,x,y,t); }else if(op==1){ x=read(); y=read(); printf("%d\n",queryMax(1,x,y)); }else{ x=read(); y=read(); printf("%lld\n",querySum(1,x,y)); } } } return 0; }

例題二、

等待完成ing…