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

2021-08-14 18:23:32 字數 3724 閱讀 4193

您需要寫一種資料結構(可參考題目標題),來維護乙個有序數列,其中需要提供以下操作:

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

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

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

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

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

第一行兩個數 n,m 表示長度為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的前驅

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

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

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 524

349分析:

樹套樹(人生中第乙個???)

剛看到題面就知道是splay

所有的查詢都是在區間上進行的,然而一棵單純的splay是不具有區間處理的優秀品質的

(查詢第k大等操作都是以一整棵splay為基礎的)

因此我們需要結合一種能進行區間操作的資料結構:線段樹

建立一棵區間線段樹

線段樹中的每個結點建立一棵splay,維護區間內的資訊

(小的在左兒子,大的在右兒子)

opt=1

直接把所有在範圍內的區間內比他小的數統計一下,+1輸出

opt=2

二分答案,通過第一種操作判定中的排名

opt=3

所有包含這個位置的區間中刪去該元素,加入新的值

opt=4

從所有在範圍內的區間中找小於ta的最大值

opt=5

從所有在範圍內的區間中找大於ta的最小值

專治手殘

#include

#include

#include

using

namespace

std;

const

int inf=1e9;

const

int n=2000003;

const

int m=50003;

int n,m,ch[n][2],pre[n],size[n],cnt[n],v[n];

int top=0,root[m<<2],a[n],maxx=0;

void clear(int bh)

int get(int bh)

void update(int bh)

void rotate(int bh)

void splay(int bh,int &root)

void insert(int &root,int x)

int last=0,now=root;

while (1)

last=now;

now=ch[now][(v[now]1:0];

if (!now)

}}int find(int x,int &root) //查詢權值x的位置

now=ch[now][(v[now]1:0];

}}int find_xiao(int x,int &root) //查詢當前區間內比x小的數有多少個

ans+=cnt[now];

now=ch[now][1];}}

}int qian(int root)

int hou(int root)

void del(int &root,int x)

if (!ch[root][0]&&!ch[root][1])

if (!ch[root][0])

if (!ch[root][1])

int t=qian(root),k=root;

splay(t,root);

ch[root][1]=ch[k][1];

pre[ch[k][1]]=root;

clear(k); update(root);

return;

}void change(int bh,int l,int r,int wz,int z)

int ask_pm(int bh,int l,int r,int l,int r,int x)

int mid=(l+r)>>1;

int ans=0;

if (l<=mid) ans+=ask_pm(bh<<1,l,mid,l,r,x);

if (r>mid) ans+=ask_pm(bh<<1|1,mid+1,r,l,r,x);

return ans;

}void solve(int l,int r,int x) //二分

printf("%d\n",l-1);

}void modify(int bh,int l,int r,int x,int z)

int next_min(int root,int x) //後繼:在ta後的最小值

else now=ch[now][1];

}return ans;

}int pre_max(int root,int x) //前驅:在ta前面的最大值

else now=ch[now][0];

}return ans;

}int find_qian(int bh,int l,int r,int l,int r,int x)

int mid=(l+r)>>1;

int ans=0;

if (l<=mid) ans=max(ans,find_qian(bh<<1,l,mid,l,r,x));

if (r>mid) ans=max(ans,find_qian(bh<<1|1,mid+1,r,l,r,x));

return ans;

}int find_hou(int bh,int l,int r,int l,int r,int x)

int mid=(l+r)>>1;

int ans=inf;

if (l<=mid) ans=min(ans,find_hou(bh<<1,l,mid,l,r,x));

if (r>mid) ans=min(ans,find_hou(bh<<1|1,mid+1,r,l,r,x));

return ans;

}int main()

for (int i=1;i<=n;i++) change(1,1,n,i,a[i]);

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

else

if (opt==4)

printf("%d\n",find_qian(1,1,n,l,r,k));

else

printf("%d\n",find_hou(1,1,n,l,r,k));

}return

0;}

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

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

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,且最小的數 額,這個題,看了一眼就知道是...