bzoj3196 二逼平衡樹 線段樹套平衡樹

2022-05-31 08:00:11 字數 4069 閱讀 5284

【題目描述】

寫一種資料結構,來維護乙個有序數列,其中需要提供以下操作:

1.查詢k在區間內的排名

2.查詢區間內排名為k的值

3.修改某一位值上的數值

4.查詢k在區間內的前驅(前驅定義為小於x,且最大的數)

5.查詢k在區間內的後繼(後繼定義為大於x,且最小的數)

【輸入格式】

第一行兩個數n,m( n,m <= 50000 )表示長度為n的有序序列和m個操作

第二行有n個數,表示有序序列

下面有m行,opt表示操作標號

若opt=1 則為操作1,之後有三個數l,r,k 表示查詢k在區間[l,r]的排名

若opt=2 則為操作2,之後有三個數l,r,k 表示查詢區間[l,r]內排名為k的數

若opt=3 則為操作3,之後有兩個數pos,k 表示將pos位置的數修改為k

若opt=4 則為操作4,之後有三個數l,r,k 表示查詢區間[l,r]內k的前驅(沒有前驅輸出no)

若opt=5 則為操作5,之後有三個數l,r,k 表示查詢區間[l,r]內k的後繼(沒有後繼輸出no)

【輸出格式】

對於操作1,2,4,5各輸出一行,表示查詢結果

sample input

9 64 2 2 1 9 4 0 1 1

2 1 4 3

3 4 10

2 1 4 3

1 2 5 9

4 3 9 5

5 2 8 5

sample output24

349hint:

1、序列中每個數的資料範圍:[0,1e8]

2、雖然原題沒有,但事實上5操作的k可能為負數

這題我在bzoj上ac,在tvoj上最後兩個點tle(然而並不知道怎麼優化)。

昨天中午打了乙個中午,晚上調了乙個鐘,今天早上又調了兩個鐘。。呵呵。。

我的做法是在開一棵1~n的線段樹t,每個節點(管理t[x].l~t[x].r)開一棵treap,把t[x].l~t[x].r的值全部放進去。為了防止找前驅後繼的時候有問題,每棵treap上先放-inf和inf進去。

1.表示查詢k在區間[l,r]的排名:用線段樹找到[l,r]所需要用到的線段樹上的節點,開乙個陣列存起來,然後乙個個區間查詢比k小的數的個數。

2.表示查詢區間[l,r]內排名為k的數:mn和mx存下出現過的最小和最大的數是什麼。二分乙個數x,然後用1的方法看x的排名。

這裡注意乙個問題:找的是排名<=k的最大的數。

例如:1 2 3 3 3 5,查詢3的排名是3,但是如果我們要找排名為4的數,答案依然是3。

3.單點修改:順著線段樹走下去,中途所經過的點,都要在它那棵treap上del原來的數,ins新數

5.找後繼:同上。

1 #include2 #include3 #include4 #include5

using

namespace

std;67

const

int n=50010,tn=50000*16+100,inf=(int

)1e9;

8struct

tr_nodec[2*tn];

11struct

xd_nodet[2*2*n];

14int

tl,cl,sl;

15int w[2*n],s[2*n];

1617

void updata(int

x)18

2324

void add(int d,int

f)25

3435

int find_ip(int d,int

id)36

45else

4650}51

return

x;52}53

54void rotate(int

x)55

7475

void splay(int x,int rt,int

id)7686}

87if(rt==0) t[id].root=x;88}

8990

void ins(int d,int

id)91

94int x=find_ip(d,id);

95if(c[x].d==d) c[x].n++;

96else

add(d,x);

97updata(x);

98 splay(x,0

,id);99}

100101

int build_xd_tree(int l,int

r)102

115else ins(-inf,x),ins(inf,x),ins(w[t[x].l],x);//

l==r

116return

x;117

}118

119void find_xd_id(int x,int l,int

r)120

122int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)>>1

;123

if(r<=mid) find_xd_id(lc,l,r);

124else

if(l>mid) find_xd_id(rc,l,r);

125else find_xd_id(lc,l,mid),find_xd_id(rc,mid+1

,r);

126}

127128

int find_qq(int d,int

id)129

136if(c[x].d>=d) x=0

;137

return

x;138

}139

140int find_hj(int d,int

id)141

148if(c[x].d<=d) x=0

;149

return

x;150

}151

152void del(int d,int

id)153

156int x0=find_qq(d,id);

157int x1=find_hj(d,id);

158 splay(x0,0

,id);

159splay(x1,x0,id);

160 c[x1].son[0]=0

;161

updata(x1);

162}

163164

void change(int x,int p,int

d)165

171int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)>>1

;172

if(p<=mid) change(lc,p,d);

173else

change(rc,p,d);

174del(w[p],x);ins(d,x);

175}

176177

int find_rk(int d,int id)//

the number of all that 178

184185

int opt1(int k,int l,int

r)186

193194

int minn(int x,int y)

195int maxx(int x,int y)

196197

int find_rank(int

k)198

204205

intmain()

206219 build_xd_tree(1

,n);

220for(int i=1;i<=m;i++)

221232

if(opt==2

)233

243 printf("

%d\n

",ll);

244}

245if(opt==3

)246

252if(opt==4

)253

260if(opt==5

)261

268}

269return0;

270 }

bzoj3196 二逼平衡樹(線段樹 splay)

您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列,其中需要提供以下操作 1.查詢k在區間內的排名 2.查詢區間內排名為k的值 3.修改某一位值上的數值 4.查詢k在區間內的前驅 前驅定義為小於x,且最大的數 5.查詢k在區間內的後繼 後繼定義為大於x,且最小的數 第一行兩個數 n,m 表示長...

BZOJ3196 二逼平衡樹 線段樹套Splay

您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列,其中需要提供以下操作 1.查詢k在區間內的排名 2.查詢區間內排名為k的值 3.修改某一位值上的數值 4.查詢k在區間內的前驅 前驅定義為小於x,且最大的數 5.查詢k在區間內的後繼 後繼定義為大於x,且最小的數 第一行兩個數 n,m 表示長...

Bzoj3196 二逼平衡樹

您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列,其中需要提供以下操作 1.查詢k在區間內的排名 2.查詢區間內排名為k的值 3.修改某一位值上的數值 4.查詢k在區間內的前驅 前驅定義為小於x,且最大的數 5.查詢k在區間內的後繼 後繼定義為大於x,且最小的數 額,這個題,看了一眼就知道是...