線段樹的區間最值操作與區間歷史最值
阿新 • • 發佈:2020-12-25
技術標籤:資料結構與演算法學習
前提知識
以下是正文
一、區間最值操作
例題一、
本題要求我們實現三個操作
對於第二種和第三種操作我們是已經知道如何寫了。
問題就在於第一種操作。
考慮第一種操作是取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…