用序列之王splay解決線段樹經典問題

2021-07-05 01:38:33 字數 3270 閱讀 3029

我們嘗試用序列之王spaly來解決線段樹經典兩個問題:

最大值和最大值2。

在n(1<=n<=100000)個數a1…an組成的序列上進行m(1<=m<=100000)次操作,操作有兩種:

1 x y:表示修改a[x]為y;

2 x y:詢問x到y之間的最大值。

這是線段樹很容易解決的問題。

我們現在考慮用splay解決。

我們設key[i]表示結點i的值。

那麼初始情況下key[i]=a[i]。

設num[i]表示以結點i為根的子樹中的最大值。

我們將n個結點弄成一條鏈。

然後旋最小的至根進行平衡調整。

接下來我們可以打出所有splay基本操作:

pd(x):判斷x是否為其父親的右子樹。

updata(x):更新結點x的值(例如num就需要更新)。

rotate(x):將結點x往上旋(需要用到函式pd)。

splay(x,y):將結點x旋至結點y下方(如果y=0相當於旋到頂)。

kth(x,y):在以結點x為根的子樹中查詢第y小的結點。

split(x,y,l,r):在以結點x為根的子樹中(注意father[x]為0),從第y小的元素後分開。讓前y個元素變為一棵splay,其餘變為一棵splay,其中前者根節點賦值給l,後者根節點賦值給r。

merge(l,r,x):將以結點l為根的splay和以結點r為根的splay合併成一棵splay(father[l]=0,father[r]=0),得到的新splay根節點賦值給x。

為了支援kth,merge操作,我們需要維護size[i]表示以i為根的子樹中有多少結點。

為了更好更優美的執行split和merge操作[即使**後兩棵splay均非空],我們在第乙個元素新增虛擬元素[虛擬結點]n+1,在最後乙個元素後新增虛擬元素[虛擬結點]n+2。

這些操作的實現不講,不懂的可以去學習然後看一下參考程式的實現。

注意的是虛擬結點和空結點的num值和key值都得賦值為-inf。

#include

#include

#include

#define fo(i,a,b) for(i=a;i<=b;i++)

#define maxn (100000+5)

#define inf (int_max)

using

namespace

std;

int tree[maxn][3],father[maxn],key[maxn],num[maxn],size[maxn];

int i,j,k,l,r,mid,t,n,m,tot,root,x,y;

int pd(int x)

void updata(int x)

void rotate(int x)

void splay(int x,int y)

}int kth(int x,int y)

void split(int x,int y,int &l,int &r)

void merge(int l,int r,int &x)

int main()

else

}tree[n+2][0]=n;

father[n]=n+2;

updata(n+2);

root=n+2;

splay(n+1,0);

root=n+1;

scanf("%d",&m);

fo(i,1,m)

else

}}

這次要求我們支援區間修改(區間都加上乙個數,可以是負數),怎麼辦?

我們學習線段樹,可以打懶標記。

因此add[i]表示以i結點為根的splay全部加上add[i]。

當乙個j需要進行splay操作時,沿途的結點就進行懶標記下傳。

那麼我們只需要在第一題的基礎上加兩個操作:

remove(x,y):在進行splay(x,y)時,對沿途的結點進行懶標記下傳。

clear(x):對結點x進行懶標記下傳。

然後我們發現並不能對,在答案為負數的情況下會輸出0。

? 其實,是因為虛擬結點它是真實不存在的,因此你不能改變它的key值。而在虛擬結點沒有左右子樹時,也不能改變它的num值。

特殊處理一下即可。具體看程式實現。

為什麼第一題不會出現這種情況?因為第一題不需要區間修改,就沒有懶標記下傳。虛擬結點的key永遠不會被修改。

#include

#include

#include

#include

#define fo(i,a,b) for(i=a;i<=b;i++)

#define maxn (100000+5)

#define inf (int_max)

using

namespace

std;

stack

s;int tree[maxn][3],father[maxn],key[maxn],num[maxn],size[maxn],add[maxn];

int i,j,k,l,r,mid,t,n,m,tot,root,x,y;

int pd(int x)

void updata(int x)

void rotate(int x)

void clear(int x)

if (tree[x][1])

add[x]=0;

}}void remove(int x,int y)while (x!=y);

while (!s.empty())

}void splay(int x,int y)

}int kth(int x,int y)

void split(int x,int y,int &l,int &r)

void merge(int l,int r,int &x)

int main()

else

}tree[n+2][0]=n;

father[n]=n+2;

updata(n+2);

root=n+2;

splay(n+1,0);

root=n+1;

scanf("%d",&m);

fo(i,1,m)

else

}}

實際上splay還可以做很多線段樹做不了的東西。

例如翻轉操作(排序機械臂)。

可以維護連通性(網路通訊)。

序列之王,維護序列(維護序列是一道題目)。

大家可以去做。

序列 線段樹

使用線段樹維護 b bb,初值為 bi b i bi 每次修改時,若乙個位置上的值變為了 0 00,則說明其會對答案產生新的貢獻,在外部使用樹狀陣列將貢獻計入答案,然後將該位置的值 重置為 bi b i bi 重置的時間複雜度是 o log n o log n o logn 考慮最壞情況,每次操作 ...

序列操作(線段樹)

lxhgww 最近收到了乙個 01 序列,序列裡面包含了 n 1 n 105 個數,這些書要麼是 0,要麼是 1,現在對這個序列有五種變換操作和詢問操作 0 a b 把 a,b 區間內所有數全部變成 0。1 a b 把 a,b 區間內所有數全部變成 1。2 a b 把 a,b 區間內所有數全部取反,...

序列操作 線段樹

題目大意 你要維護乙個長度為n的序列,進行操作。對於這個序列,233之類的數不能出現,也就是說233,2333,23333,233333 這一系列的數不能在序列中出現。1 i 輸出第i個元素。2 a b x 將 a,b 區間的序列賦值為x。3 a b x 將 a,b 區間的序列加上x。4 a b 將...