1. 程式人生 > >伸展樹基本概念基本題目

伸展樹基本概念基本題目

name names algorithm rto 維護 每次 等於 http 移動

http://blog.csdn.net/discreeter/article/details/51524210 //基本概念詳見這裏

例題HDU4453 代碼來源http://blog.csdn.net/auto_ac/article/details/12318809

伸展樹我個人理解就是每次查詢或更改都要將其移動至根節點 另外伸展樹有單點操作和區間操作 維護的是一個中序遍歷(這點很重要)

旋轉操作的話結合概念和代碼還是很清晰的,有左旋(當要進行旋轉操作的是其根節點的右節點),右旋(當要進行旋轉操作的是其根節點的左節點),還有一字型旋轉和之字形旋轉,詳見基本概念

情況一:節點x的父節點y是根節點。這時,如果x是y的左孩子,我們進行一次Zig(右旋)操作;如果x 是y 的右孩子,則我們進行一次Zag(左旋)操作。經過旋轉,x成為二叉查找樹S的根節點,調整結束。即:如果當前結點父結點即為根結點,那麽我們只需要進行一次簡單旋轉即可完成任務,我們稱這種旋轉為單旋轉。如圖1所示

技術分享

(圖1)

情況二:節點x 的父節點y 不是根節點,y 的父節點為z,且x 與y 同時是各自父節點的左孩子或者同時是各自父節點的右孩子。這時,我們進行一次Zig-Zig操作或者Zag-Zag操作。即:設當前結點為X , X 的父結點為Y ,Y 的父結點為Z ,如果Y 和X 同為其父親的左孩子或右孩子,那麽我們先旋轉Y ,再旋轉X 。我們稱這種旋轉為一字形旋轉。如圖2所示

技術分享

(圖2)

情況三:節點x的父節點y不是根節點,y的父節點為z,x與y中一個是其父節點的左孩子而另一個是其父節點的右孩子。這時,我們進行一次Zig-Zag操作或者Zag-Zig 操作。即:這時我們連續旋轉兩次X 。我們稱這種旋轉為之字形旋轉。如圖3所示

技術分享

(圖3)

如圖4所示,執行Splay(1,S),我們將元素1 調整到了伸展樹S 的根部。再執行Splay(2,S),如圖5 所示,我們從直觀上可以看出在經過調整後,伸展樹比原來“平衡”了許多。而伸展操作的過程並不復雜,只需要根據情況進行旋轉就可以了,而三種旋轉都是由基本得左旋和右旋組成的,實現較為簡單。

技術分享

(圖4)

技術分享

(圖5)

區間操作的圖解原網站也很清晰:

首先我們認為伸展樹的中序遍歷即為我們維護的數列,那麽很重要的一個操作就是怎麽在伸展樹中表示任意一個區間。比如我們要提取區間a,b],那麽我們將a前面一個數對應的結點轉到樹根,將b 後面一個結點對應的結點轉到樹根的右邊,那麽根右邊的左子樹就對應了區間[a,b]。其中的道理也是很簡單的,將a 前面一個數對應的結點轉到樹根後, a 及a 後面的數就在根的右子樹上,然後又將b後面一個結點對應的結點轉到樹根的右邊,那麽[a,b]這個區間就是圖8中*所示的子樹。

利用這個,我們就可以實現線段樹的一些功能,比如回答對區間的詢問。我們在每個結點上記錄關於以這個結點為根的子樹的信息,然後詢問時先提取區間,再直接讀取子樹的相關信息。還可以對區間進行整體修改,這也要用到和線段樹類似的延遲標記技術,就是對於每個結點,再額外記錄一個或多個標記,表示以這個結點為根的子樹是否被進行了某種操作,並且這種操作影響其子結點的信息值。當然,既然記錄了標記,那麽旋轉和其他一些操作中也就要相應地將標記向下傳遞。

技術分享

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define KT ch[ ch[root][1] ][0]
#define L ch[x][0]
#define R ch[x][1]
const int maxn = 300005;
int num[maxn], n, m;
int k1, k2;
typedef long long ll;
struct splayTree {
int ch[maxn][2], sz[maxn], pre[maxn];
int root, tot, all; // all:節點總數, tot:最大標號

int add[maxn], val[maxn];
bool flip[maxn]; //翻轉標記
int sta[maxn], top;
void rotate(int &x, int f) {//f等於一是右旋
int y = pre[x], z = pre[y];
down(y); down(x);
ch[y][!f] = ch[x][f];
pre[ch[x][f]] = y;
pre[x] = pre[y];
if(z) ch[z][ch[z][1] == y] = x;
ch[x][f] = y;
pre[y] = x;
up(y);
}
void splay(int &x, int g) {
down(x);
while(pre[x] != g) {
int y = pre[x], z = pre[y];
down(z); down(y); down(x);
if(z == g) {
rotate(x, ch[y][0] == x);
}
else {
int f = (ch[z][0] == y);
ch[y][!f] == x ? rotate(y, f) : rotate(x, !f);
rotate(x, f);
}
}
if(!g) root = x;
up(x);
}
void rto(int k, int g) {
int x = root;
while(1) {
down(x);

if(sz[L] == k) break;
if(sz[L] > k) x = L;
else {
k -= sz[L] + 1;
x = R;
}
}
splay(x, g);
}
void down(int x){
if(add[x]) {
val[L] += add[x];
val[R] += add[x];
add[L] += add[x];
add[R] += add[x];
add[x] = 0;
}
if(flip[x]) {
flip[L] ^= 1;
flip[R] ^= 1;
swap(L, R);
flip[x] = 0;
}
}
void up(int x) {
sz[x] = sz[L] + sz[R] +1;
}

void build(int &x, int l, int r, int fa) {
if(l > r) return;
int m = (l+r) >> 1;
newNode(x, num[m], fa);
build(L, l, m-1, x);
build(R, m+1, r, x);
up(x);
}
void newNode(int &x, int v, int fa) {
if(top) x = sta[top--]; //內存回收
else x = ++tot;
all++;
pre[x] = fa;
sz[x] = 1;
L = R = 0;
val[x] = v;
add[x] = 0;
flip[x] = 0;
}
void init(int n) {
all = tot = top = 0;
newNode(root, 0, 0);
// all:節點總數, tot:最大標號
newNode(ch[root][1], 0, root);
for(int i = 0; i < n; i++)
scanf("%d", &num[i]);
build(KT, 0, n-1, ch[root][1]);
up(ch[root][1]);
up(root);
debug();
}
ll erase(int k) { //刪除第k個數
rto(k, 0);
rto(k-1, root);
sta[++top] = root;
all--;
ll ret = val[root];
int ls = ch[root][0], rs = ch[root][1];
root = ls;
pre[ls] = 0;
ch[ls][1] = rs;
if(rs)pre[rs] = ls;
up(root);
return ret;
}
void insert(int k, int v) { //在第k個數後面插入一個數v
rto(k, 0);
int x;
int rs = ch[root][1];
newNode(x, v, root);
ch[root][1] = x;
ch[x][1] = rs;
if(rs) pre[ch[x][1]] = x;
up(ch[root][1]);
up(root);
}
void move1() {
int v = erase(all-2);
insert(0, v);
}
void move2() {
int v = erase(1);
insert(all-2, v);

}
void update(int l, int r, int v) {
rto(l-1, 0);
debug();
rto(r+1, root);
debug();
val[KT] += v;
add[KT] += v;
up(ch[root][1]);
up(root);
}
void reverse(int l, int r) {
rto(l-1, 0);
debug();
rto(r+1, root);
debug();
flip[KT] ^= 1;
up(ch[root][1]);
up(root);
}
int query() {
rto(1, 0);
return val[root];
}
void print(int x) {
printf("node %d: ls=%d rs=%d lsz = %d rsz = %d val = %d\n", x, L, R, sz[L], sz[R], val[x]);
if(L)print(L);
if(R)print(R);
}
void debug() {
printf("root = %d\n", root);
print(root);
}
}spt;
int main() {
int ca = 1;
while(~scanf("%d%d%d%d", &n, &m, &k1, &k2)) {
if(!m && !n && !k1 && !k2) break;
char op[111];
spt.init(n);
//spt.debug();
printf("Case #%d:\n", ca++);
while(m--) {
scanf("%s", op);
if(op[0] == ‘q‘) {
printf("%d\n", spt.query());
spt.debug();
}
if(op[0] == ‘m‘) {
int kd;
scanf("%d", &kd);
if(kd == 1) spt.move1();
else spt.move2();
spt.debug();
}
if(op[0] == ‘i‘) {
int v;
scanf("%d", &v);
spt.insert(1, v);
spt.debug();
}
if(op[0] == ‘d‘)
{spt.erase(1);spt.debug();}
if(op[0] == ‘a‘) {
int v;
scanf("%d", &v);
spt.update(1, k2, v);
spt.debug();
}
if(op[0] == ‘r‘)
spt.reverse(1, k1);
}
}
return 0;
}

伸展樹基本概念基本題目