區間翻轉問題 Splay

2021-08-11 07:12:22 字數 2847 閱讀 4018

問題描述

給你乙個長度為n的序列和m個操作

1.查詢第k個數的值

2.將第k個數增加d

3.查詢一段區間的和

4.查詢一段區間的最大值

5.將一段區間鏡面翻轉(例如序列,將從2到5的區間翻轉後得到序列)

對於除操作2,5以外的操作,輸出相應的答案

輸入格式

第一行兩個正整數n,m

第二行n個整數,為初始的序列

第三行到底m+2行,每行若干個整數

·如果第乙個數為1,那麼後面乙個正整數k,表示查詢第k個數的值

·如果第乙個數是2,那麼後面兩個正整數k,d,表示將ak增加d

·如果第乙個數為3,那麼後面兩個正整數l,r,表示查詢從al到ar的區間和

·如果第乙個數為4,那麼後面兩個正整數l,r,表示查詢從al到ar的最大值

·如果第乙個數為5,那麼後面兩個正整數l,r,表示翻轉從al到ar的這個區間

輸出格式

除操作2,5外每個操作輸出佔一行,乙個整數,為本次提問的答案

樣例輸入

6 8

1 2 3 4 5 6

1 4

3 2 5

4 2 2

5 2 5

3 1 3

5 2 5

2 5 1

4 1 6

樣例輸出

4

14 2

10 6

資料規模

2<=n<=100000

1<=m<=100000

原序列1<=ai<=1000

每次1<=k<=n,1<=l<=r<=n,1<=d<=1000

**

感謝nodgd命題並提供資料

splay裸題,主要是留個紀念。

首先按照編號建立平衡樹,具體方法是加兩個虛擬節點1,n+2,再將原數列中的數按key值為編號+1插入平衡樹。

對於區間操作的整體思想是:提取乙個區間[l,r],就把key為(l+1)-1的節點旋轉到根,再把key為(r+1)+1的節點旋轉到根的右兒子。此時這個節點的左子樹就正好是需要提取的區間。這個由二叉檢索樹的性質是正確的。

於是,要得到區間和、區間極值,只需要維護以某個節點為根的子樹的和、子樹中的權值最大值即可。這些容易在旋轉的同時更新。

修改某個點的權值,我採用的是先將該點旋轉到根,再更新根節點的max,sum,val值。也可以在查詢該點的同時更新路徑上所有點的max,sum值。

最後是區間翻轉問題。提取區間後,只需要打上lazy標記,訪問到的時候下放,交換左右兩棵子樹即可。雖然splay是自下往上的過程,但是splay前一定要先找到需要splay到根的點。這個過程是自上往下的,此時下放即可,不會造成操作的衝突。

注意lazy與線段樹里略有不同:線段樹中打上lazy標記的點一定是已經處理完該點的操作了,而本題中是下放時才進行操作。所以打lazy標記時應當用lazy^=1而不是lazy=1。

**:

#include#include#define maxn 100005

using namespace std;

int tmax(int x,int y,int z)

int n;

int rt,tot,fa[maxn],ls[maxn],rs[maxn],pri[maxn],val[maxn],lazy[maxn],sum[maxn],max[maxn],size[maxn];

void update(int x,int y)

void putdown(int p)

void zig(int x)

fa[x]=z;fa[y]=x;fa[rs[x]]=y;

ls[y]=rs[x];rs[x]=y;

update(x,y);

}void zag(int x)

fa[x]=z;fa[y]=x;fa[ls[x]]=y;

rs[y]=ls[x];ls[x]=y;

update(x,y);

}void splay(int x,int t)

else

else}}

if(!t)rt=x;

}void ins(int k,int v)

p=ls[p];

}else

p=rs[p];}}

size[tot]=1;fa[tot]=p;pri[tot]=k;val[tot]=max[tot]=sum[tot]=v;

splay(tot,0);

}int getkth(int k)

}void add(int k,int d)

void prepare(int x,int y)

int getsum(int x,int y)

int getmax(int x,int y)

void rev(int x,int y)

int main()

while(m--)

else

if(op==2)

else

if(op==3)

else

if(op==4)

else

}}

Splay之區間翻轉問題

給你 n 個數 分別為 1,2,3,4 n 然後有 m 個操作,每個操作對應一段區間,將區間的數進行翻轉。比如 1,2,3,4,5 這段序列 操作區間為 2 4 翻轉完成後的序列為 1,4,3,2,5 第一行分別輸入 n,m 第2行到m 1行每行輸入兩個數 對應操作的區間 輸出最後翻轉完成後的序列 ...

平衡樹 Splay 區間翻轉 模板

luogup3391一道模板題 學習了spl ay splay spla y區間翻轉的姿勢 具體就是先建兩個虛擬節點1,n 2 1,n 2 1,n 2,然後按節點下標 1 1 1將區間樹建出來,這時候排名為k kk的就是第k kk個數。翻轉的時候先把 l 1 1 l 1 1 l 1 1提到根,再把 ...

區間翻轉問題

給你乙個長度為n的序列和m個操作 1.查詢第k個數的值 2.將第k個數增加d 3.查詢一段區間的和 4.查詢一段區間的最大值 5.將一段區間鏡面翻轉 例如序列,將從2到5的區間翻轉後得到序列 對於除操作2,5以外的操作,輸出相應的答案 第一行兩個正整數n,m 第二行n個整數,為初始的序列 第三行到底...