普通平衡樹
解題思路
平衡樹模板題,我分別用了 [非旋Treap] 和 [Splay] AC了本題。
一、Splay
每個節點表示一個值,同時記錄該點及其子樹大小、該點表示的值的出現次數、左右兒子、父節點。
- 插入:將x前驅旋至根,x後繼旋至根的右兒子,那麼根的左兒子的右兒子即爲要插入的位置,如果此位置上無數,則新建一個節點;否則該位置的出現次數和大小加1。
- 注意:爲了避免找不到x前驅和後繼,應事先插入一個值爲-INF和值爲INF的節點。
- 刪除:將x前驅旋至根,x後繼旋至根的右兒子,那麼根的左兒子的右兒子即爲要刪除的節點,如果此節點大小爲1,直接刪除;否則該位置的出現次數和大小減1。
- 查x排名:將x旋至根,則x排名爲根的左兒子大小+1
- 查排名爲x的數:從根向下查找,如果當前節點的左兒子大小+1=x,則返回當前節點的值;否則,如果當前節點的左兒子大小 x,則向其右兒子查找;否則,向其左兒子查找。
- 求x前驅:從根向下查找,如果當前節點的值小於等於x,更新ans並向其右兒子查找;否則,向其左兒子查找。更新時,不斷取max即可。
- 求x後繼:從根向下查找,如果當前節點的值大於等於x,更新ans並向其左兒子查找;否則,向其右兒子查找。更新時,不斷取min即可。
時間複雜度:每次操作
Code#1
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 0x7fffffff;
const int N = 100005;
int n, opt, q;
int cnt = 0, root = 0;
struct Splay_tree{
int fa, son[2], size, val, times;
}tr[N];
inline void pushup(int x){
if(x){
tr[x].size = tr[x].times;
if(tr[x].son[0]) tr[x].size += tr[tr[x].son[0]].size;
if(tr[x].son[1]) tr[x].size += tr[tr[x].son[1]].size;
}
}
inline void rotate(int x, int kind){
int y = tr[x].fa, z = tr[y].fa, A = tr[y].son[kind], B = tr[x].son[kind], C = tr[x].son[!kind];
tr[x].son[kind] = y, tr[x].fa = z;
tr[y].son[!kind] = B, tr[y].fa = x, tr[B].fa = y;
tr[z].son[tr[z].son[1] == y] = x;
pushup(y), pushup(x);
}
inline void splay(int x, int goal){
if(x == goal) return;
while(tr[x].fa != goal){
int y = tr[x].fa, z = tr[y].fa;
int isrson1 = tr[y].son[1] == x, isrson2 = tr[z].son[1] == y;
if(z == goal) rotate(x, !isrson1);
else{
if(isrson1 == isrson2) rotate(y, !isrson2);
else rotate(x, !isrson1);
rotate(x, !isrson2);
}
}
if(goal == 0) root = x;
}
inline int select(int x){
int now = root;
while(now){
if(tr[now].val == x) break;
else if(tr[now].val < x) now = tr[now].son[1];
else if(tr[now].val > x) now = tr[now].son[0];
}
if(!now) return -1;
return now;
}
inline int getPre(int x){
int now = root, ans = -INF;
while(now){
if(tr[now].val < x){
ans = max(ans, tr[now].val);
now = tr[now].son[1];
}
else now = tr[now].son[0];
}
return ans;
}
inline int getSub(int x){
int now = root, ans = INF;
while(now){
if(tr[now].val > x){
ans = min(ans, tr[now].val);
now = tr[now].son[0];
}
else now = tr[now].son[1];
}
return ans;
}
inline int getRank(int x){
int now = root, ans = 0;
while(now){
if(tr[now].val == x){
ans += tr[tr[now].son[0]].size + 1;
break;
}
else if(tr[now].val < x){
ans += tr[tr[now].son[0]].size + tr[now].times;
now = tr[now].son[1];
}
else now = tr[now].son[0];
}
return ans - 1;
}
inline int newNode(int val, int f){
++cnt;
tr[cnt].val = val;
tr[cnt].fa = f;
tr[cnt].son[0] = tr[cnt].son[1] = 0;
tr[cnt].size = tr[cnt].times = 1;
return cnt;
}
inline void insert(int x){
splay(select(getPre(x)), 0);
splay(select(getSub(x)), root);
int t = tr[tr[root].son[1]].son[0];
if(!t)
tr[tr[root].son[1]].son[0] = newNode(x, tr[root].son[1]);
else tr[t].times++, tr[t].size++;
pushup(tr[root].son[1]);
pushup(root);
}
inline void del(int x){
splay(select(getPre(x)), 0);
splay(select(getSub(x)), root);
int t = tr[tr[root].son[1]].son[0];
if(!t || tr[t].times == 0) return;
tr[t].times--, tr[t].size--;
if(tr[t].times == 0) tr[tr[root].son[1]].son[0] = 0;
pushup(tr[root].son[1]);
pushup(root);
}
inline int findRank(int x){
int now = root;
while(now){
if(tr[tr[now].son[0]].size + 1 <= x && x <= tr[tr[now].son[0]].size + tr[now].times) break;
else if(tr[tr[now].son[0]].size + 1 > x)
now = tr[now].son[0];
else if(tr[tr[now].son[0]].size + tr[now].times < x){
x -= tr[tr[now].son[0]].size + tr[now].times;
now = tr[now].son[1];
}
}
return tr[now].val;
}
int main(){
scanf("%d", &n);
root = newNode(-INF, 0);
tr[root].son[1] = newNode(INF, root), pushup(root);
while(n--){
scanf("%d%d", &opt, &q);
if(opt == 1) insert(q);
else if(opt == 2) del(q);
else if(opt == 3) printf("%d\n", getRank(q));
else if(opt == 4) printf("%d\n", findRank(q+1));
else if(opt == 5) printf("%d\n", getPre(q));
else if(opt == 6) printf("%d\n", getSub(q));
}
return 0;
}
二、非旋Treap
每個節點表示一個值,同時記錄該點及其子樹大小、左右兒子。
- 插入:從x處split,新建一個值爲x的節點,再將三部分merge起來。(注:從x處分開:x在前一部分,下同)
- 刪除:從x、x+1處split成三部分(記爲l、t、r),將t的左右兒子merge起來,這樣就刪除了一個節點,再將三部分merge起來。
- 查x排名:從x-1處split,則x排名爲前一部分的大小+1
- 查排名爲x的數:同Splay
- 求x前驅:同Splay
- 求x後繼:同Splay
時間複雜度:每次操作
Code#2
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 1e9;
const int N = 100005;
int n, opt, q;
struct Treap{
int val, son[2], size, hp;
}tr[N];
struct OPT_Treap{
int cnt, root;
inline int newNode(int val){
cnt++;
tr[cnt].val = val;
tr[cnt].hp = rand();
tr[cnt].son[0] = tr[cnt].son[1] = 0;
tr[cnt].size = 1;
return cnt;
}
inline void pushup(int id){
tr[id].size = 1;
if(tr[id].son[0]) tr[id].size += tr[tr[id].son[0]].size;
if(tr[id].son[1]) tr[id].size += tr[tr[id].son[1]].size;
}
inline void pushdown(int id){
return;
}
int merge(int a, int b){
if(a == 0) return b;
if(b == 0) return a;
if(tr[a].hp <= tr[b].hp){
pushdown(a);
tr[a].son[1] = merge(tr[a].son[1], b);
pushup(a);
return a;
}
else{
pushdown(b);
tr[b].son[0] = merge(a, tr[b].son[0]);
pushup(b);
return b;
}
}
void split(int id, int k, int &x, int &y){
if(!id){
x = 0, y = 0;
return;
}
pushdown(id);
if(tr[id].val > k)
y = id, split(tr[id].son[0], k, x, tr[id].son[0]);
else
x = id, split(tr[id].son[1], k, tr[id].son[1], y);
pushup(id);
}
inline void insert(int val){
int l = 0, r = 0;
split(root, val, l, r);
int t = newNode(val);
root = merge(merge(l, t), r);
}
inline void del(int val){
int l = 0, r = 0, t = 0;
split(root, val - 1, l, t);
split(t, val, t, r);
t = merge(tr[t].son[0], tr[t].son[1]);
root = merge(merge(l, t), r);
}
inline int getRank(int x){
int ans = 0, l = 0, r = 0;
split(root, x-1, l, r);
ans = tr[l].size + 1;
root = merge(l, r);
return ans;
}
inline int getKth(int k){
int now = root;
while(now){
if(tr[tr[now].son[0]].size + 1 == k) return tr[now].val;
else if(tr[tr[now].son[0]].size >= k) now = tr[now].son[0];
else k -= (tr[tr[now].son[0]].size + 1), now = tr[now].son[1];
}
return -INF;
}
inline int getPre(int x){
int ans = -INF, now = root;
while(now){
if(tr[now].val >= x) now = tr[now].son[0];
else{
ans = max(ans, tr[now].val);
now = tr[now].son[1];
}
}
return ans;
}
inline int getSub(int x){
int ans = INF, now = root;
while(now){
if(tr[now].val <= x) now = tr[now].son[1];
else{
ans = min(ans, tr[now].val);
now = tr[now].son[0];
}
}
return ans;
}
}BST;
int main(){
srand(200127);
scanf("%d", &n);
BST.root = BST.newNode(INF);
while(n--){
scanf("%d%d", &opt, &q);
if(opt == 1) BST.insert(q);
else if(opt == 2) BST.del(q);
else if(opt == 3) printf("%d\n", BST.getRank(q));
else if(opt == 4) printf("%d\n", BST.getKth(q));
else if(opt == 5) printf("%d\n", BST.getPre(q));
else if(opt == 6) printf("%d\n", BST.getSub(q));
}
return 0;
}
文藝平衡樹
解題思路
一、Splay
這道題只有區間翻轉操作,線段樹不好維護,只有用平衡樹了。
對於一次 的區間翻轉,把 旋至根, 旋至根的右兒子,那麼 就在根的右兒子的左兒子處了。和線段樹一樣,我們可以將它的左右兒子互換後打上一個翻轉標記(rev ^= 1),之後再pushdown。
Code#3
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 0x7fffffff;
const int N = 100005;
int n, m, ql, qr;
int cnt, root;
struct Splay_tree{
int fa, son[2], size, val;
bool rev;
}tr[N];
inline void pushup(int x){
if(x){
tr[x].size = 1;
if(tr[x].son[0]) tr[x].size += tr[tr[x].son[0]].size;
if(tr[x].son[1]) tr[x].size += tr[tr[x].son[1]].size;
}
}
inline void pushdown(int x){
if(tr[x].rev){
if(tr[x].son[0]){
tr[tr[x].son[0]].rev ^= 1;
swap(tr[tr[x].son[0]].son[0], tr[tr[x].son[0]].son[1]);
}
if(tr[x].son[1]){
tr[tr[x].son[1]].rev ^= 1;
swap(tr[tr[x].son[1]].son[0], tr[tr[x].son[1]].son[1]);
}
tr[x].rev = 0;
}
}
inline void rotate(int x, int kind){
int y = tr[x].fa, z = tr[y].fa, A = tr[y].son[kind], B = tr[x].son[kind], C = tr[x].son[!kind];
tr[x].son[kind] = y, tr[x].fa = z;
tr[y].son[!kind] = B, tr[y].fa = x;
tr[z].son[tr[z].son[1] == y] = x;
tr[B].fa = y;
pushup(y), pushup(x);
}
inline void splay(int x, int goal){
if(x == goal) return;
while(tr[x].fa != goal){
int y = tr[x].fa, z = tr[y].fa;
pushdown(z), pushdown(y), pushdown(x);
int isrson1 = tr[y].son[1] == x, isrson2 = tr[z].son[1] == y;
if(z == goal) rotate(x, !isrson1);
else{
if(isrson1 == isrson2) rotate(y, !isrson2);
else rotate(x, !isrson1);
rotate(x, !isrson2);
}
}
if(goal == 0) root = x;
}
inline int newNode(int val, int f){
cnt++;
tr[cnt].val = val;
tr[cnt].fa = f;
tr[cnt].son[0] = tr[cnt].son[1] = 0;
tr[cnt].size = 1;
return cnt;
}
int select(int x){
int now = root;
pushdown(now);
while(tr[tr[now].son[0]].size + 1 != x){
if(tr[tr[now].son[0]].size + 1 > x) now = tr[now].son[0];
else{
x -= tr[tr[now].son[0]].size + 1;
now = tr[now].son[1];
}
pushdown(now);
}
return now;
}
inline void reverse(int l, int r){
splay(select(l-1), 0);
splay(select(r+1), root);
int t = tr[tr[root].son[1]].son[0];
tr[t].rev ^= 1;
swap(tr[t].son[0], tr[t].son[1]);
}
int build(int l, int r, int f){
if(l > r) return 0;
int mid = (l + r) >> 1, x = ++cnt;
tr[x].val = mid - 1;
tr[x].size = 1;
tr[x].fa = f;
tr[x].rev = 0;
tr[x].son[0] = build(l, mid-1, x);
tr[x].son[1] = build(mid+1, r, x);
pushup(x);
return x;
}
void print(int x){
pushdown(x);
if(tr[x].son[0]) print(tr[x].son[0]);
if(tr[x].val >= 1 && tr[x].val <= n) printf("%d ", tr[x].val);
if(tr[x].son[1]) print(tr[x].son[1]);
}
int main(){
scanf("%d%d", &n, &m);
root = build(1, n+2, 0);
while(m--){
scanf("%d%d", &ql, &qr);
reverse(ql+1, qr+1);
}
print(root);
return 0;
}
二、非旋Treap
同上。
Code#4
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 1e9;
const int N = 100005;
int n, m, ql, qr;
struct Treap{
int val, size, son[2], hp;
bool rev;
}tr[N];
struct OPT_Treap{
int cnt, root;
inline int newNode(int val){
cnt++;
tr[cnt].val = val;
tr[cnt].hp = rand();
tr[cnt].size = 1;
tr[cnt].son[0] = tr[cnt].son[1] = 0;
tr[cnt].rev = 0;
return cnt;
}
inline void pushup(int id){
if(!id) return;
tr[id].size = 1;
if(tr[id].son[0]) tr[id].size += tr[tr[id].son[0]].size;
if(tr[id].son[1]) tr[id].size += tr[tr[id].son[1]].size;
}
inline void pushdown(int id){
if(!tr[id].rev) return;
if(tr[id].son[0]){
int t = tr[id].son[0];
tr[t].rev ^= 1;
swap(tr[t].son[0], tr[t].son[1]);
}
if(tr[id].son[1]){
int t = tr[id].son[1];
tr[t].rev ^= 1;
swap(tr[t].son[0], tr[t].son[1]);
}
tr[id].rev ^= 1;
}
int merge(int a, int b){
if(a == 0) return b;
if(b == 0) return a;
if(tr[a].hp <= tr[b].hp){
pushdown(a);
tr[a].son[1] = merge(tr[a].son[1], b);
pushup(a);
return a;
}
else{
pushdown(b);
tr[b].son[0] = merge(a, tr[b].son[0]);
pushup(b);
return b;
}
}
void split(int id, int k, int &x, int &y){
if(!id){
x = 0, y = 0;
return;
}
pushdown(id);
if(tr[tr[id].son[0]].size >= k)
y = id, split(tr[id].son[0], k, x, tr[id].son[0]);
else
x = id, split(tr[id].son[1], k - tr[tr[id].son[0]].size - 1, tr[id].son[1], y);
pushup(id);
}
inline void reverse(int l, int r){
int L, t, R;
split(root, l - 1, L, t);
split(t, r - l + 1, t, R);
tr[t].rev ^= 1;
swap(tr[t].son[0], tr[t].son[1]);
root = merge(merge(L, t), R);
}
inline int build(int l, int r){
if(l > r) return 0;
int mid = (l + r) >> 1;
int t = newNode(mid);
tr[t].son[0] = build(l, mid - 1);
tr[t].son[1] = build(mid + 1, r);
pushup(t);
return t;
}
}BST;
void print(int x){
BST.pushdown(x);
if(tr[x].son[0]) print(tr[x].son[0]);
printf("%d ", tr[x].val);
if(tr[x].son[1]) print(tr[x].son[1]);
}
int main(){
srand(200127);
scanf("%d%d", &n, &m);
BST.root = BST.build(1, n);
while(m--){
scanf("%d%d", &ql, &qr);
BST.reverse(ql, qr);
}
print(BST.root);
return 0;
}
二逼平衡樹
解題思路
一、線段樹套Splay
這道題與普通平衡樹唯一的不同就在於所有查詢都是區間查詢,那麼我們需要在平衡樹外面套一層線段樹以供區間查詢,即線段樹套平衡樹。
當然,並非真的要在每個線段樹節點內建一顆平衡樹,存一下在這個節點的平衡樹的根的編號就行了。
- 查詢區間內k的排名:在線段樹上遞歸找查詢的區間,在相應節點上的平衡樹上查詢比k小的數的個數,回溯時將所有答案相加得到了區間內比k小的數的個數,最後+1就是排名;
- 查詢區間內排名爲k的值:這個要麻煩一點,由於不同線段樹節點上的答案不能進行合併,只能考慮二分答案,問題轉化爲二分出的答案在區間內的排名問題,即第一問;
- 修改某位置的值:修改即先刪除原值,再插入新值;在線段樹上找到該節點,對所經路線上所有線段樹裏的平衡樹進行刪除插入操作;
- 查詢k在區間內的前驅:同第一問,只不過在更新答案時不是相加,而是取max;
- 查詢k在區間內的後繼:同第一問,只不過在更新答案時不是相加,而是取min。
Code#5
#include<cstdio>
#include<iostream>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
#define mid ((A[id].l+A[id].r)>>1)
using namespace std;
const int INF = 0x7fffffff;
const int N = 50005;
int n, m, a[N], opt, ql, qr, qk, qpos, tmp;
struct splay{
int size, times, val, son[2], fa;
}B[(int)4e6];
struct segTree{
int l, r, root;
}A[N<<2];
struct OPT_splay{
int cnt;
inline void pushup(int x){
if(x){
B[x].size = B[x].times;
if(B[x].son[0]) B[x].size += B[B[x].son[0]].size;
if(B[x].son[1]) B[x].size += B[B[x].son[1]].size;
}
}
inline void rotate(int x, int kind){
int y = B[x].fa, z = B[y].fa, a = B[y].son[kind], b = B[x].son[kind], c = B[x].son[!kind];
B[x].fa = z, B[x].son[kind] = y;
B[y].fa = x, B[y].son[!kind] = b;
B[z].son[B[z].son[1] == y] = x;
B[b].fa = y;
pushup(y), pushup(x);
}
inline void splay(int x, int goal, int id){
if(x == goal) return;
while(B[x].fa != goal){
int y = B[x].fa, z = B[y].fa;
int isrson1 = B[y].son[1] == x, isrson2 = B[z].son[1] == y;
if(z == goal) rotate(x, !isrson1);
else{
if(isrson1 == isrson2) rotate(y, !isrson2);
else rotate(x, !isrson1);
rotate(x, !isrson2);
}
}
if(goal == 0) A[id].root = x;
}
inline int newNode(int val, int fa){
cnt++;
B[cnt].fa = fa;
B[cnt].val = val;
B[cnt].size = B[cnt].times = 1;
B[cnt].son[0] = B[cnt].son[1] = 0;
return cnt;
}
inline int getPre(int x, int id){
int now = A[id].root, res = -INF;
while(now){
if(B[now].val < x){
res = max(res, B[now].val);
now = B[now].son[1];
}
else now = B[now].son[0];
}
return res;
}
inline int getSub(int x, int id){
int now = A[id].root, res = INF;
while(now){
if(B[now].val > x){
res = min(res, B[now].val);
now = B[now].son[0];
}
else now = B[now].son[1];
}
return res;
}
inline int select(int x, int id){
int now = A[id].root;
while(now){
if(B[now].val == x) break;
else if(B[now].val > x) now = B[now].son[0];
else if(B[now].val < x) now = B[now].son[1];
}
if(!now) return -1;
return now;
}
inline int getRank(int x, int id){
if(select(x, id) != -1) splay(select(x, id), 0, id);
else splay(select(getSub(x, id), id), 0, id);
return B[B[A[id].root].son[0]].size;
}
inline void insert(int val, int id){
splay(select(getPre(val, id), id), 0, id);
splay(select(getSub(val, id), id), A[id].root, id);
int t = B[B[A[id].root].son[1]].son[0];
if(!t) B[B[A[id].root].son[1]].son[0] = newNode(val, B[A[id].root].son[1]);
else B[t].times++, B[t].size++;
pushup(B[A[id].root].son[1]);
pushup(A[id].root);
}
inline void del(int val, int id){
splay(select(getPre(val, id), id), 0, id);
splay(select(getSub(val, id), id), A[id].root, id);
int t = B[B[A[id].root].son[1]].son[0];
if(!t || B[t].times == 0) return;
B[t].times--, B[t].size--;
if(B[t].times == 0) B[B[A[id].root].son[1]].son[0] = 0;
pushup(B[A[id].root].son[1]);
pushup(A[id].root);
}
}Splay;
struct OPT_segTree{
void build(int id, int l, int r){
A[id].root = Splay.newNode(-INF, 0);
B[A[id].root].son[1] = Splay.newNode(INF, A[id].root);
A[id].l = l, A[id].r = r;
if(A[id].l == A[id].r) return;
build(lid, l, mid);
build(rid, mid+1, r);
}
void insert(int id, int pos, int val){
Splay.insert(val, id);
if(A[id].l == A[id].r) return;
if(pos <= mid) insert(lid, pos, val);
else insert(rid, pos, val);
}
int getRank(int id, int l, int r, int x){
if(A[id].l == l && A[id].r == r) return Splay.getRank(x, id) - 1;
if(r <= mid) return getRank(lid, l, r, x);
else if(l > mid) return getRank(rid, l, r, x);
else return getRank(lid, l, mid, x) + getRank(rid, mid+1, r, x);
}
int getKth(int l, int r, int k){
int ans = -1, L = 0, R = 1e8;
while(L <= R){
int Mid = (L + R) >> 1;
int t1 = getRank(1, l, r, Mid) + 1;
int t2 = getRank(1, l, r, Mid+1);
if(t1 <= k && k <= t2){ ans = Mid; break; }
if(t2 < k) L = Mid+1;
else if(t1 > k) R = Mid-1;
}
return ans;
}
void modify(int id, int pos, int val){
Splay.del(a[pos], id);
Splay.insert(val, id);
if(A[id].l == A[id].r) return;
if(pos <= mid) modify(lid, pos, val);
else modify(rid, pos, val);
}
int getPre(int id, int l, int r, int x){
if(A[id].l == l && A[id].r == r) return Splay.getPre(x, id);
if(r <= mid) return getPre(lid, l, r, x);
else if(l > mid) return getPre(rid, l, r, x);
else return max(getPre(lid, l, mid, x), getPre(rid, mid+1, r, x));
}
int getSub(int id, int l, int r, int x){
if(A[id].l == l && A[id].r == r) return Splay.getSub(x, id);
if(r <= mid) return getSub(lid, l, r, x);
else if(l > mid) return getSub(rid, l, r, x);
else return min(getSub(lid, l, mid, x), getSub(rid, mid+1, r, x));
}
}Seg;
int main(){
scanf("%d%d", &n, &m);
Seg.build(1, 1, n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
Seg.insert(1, i, a[i]);
}
while(m--){
scanf("%d", &opt);
if(opt == 3) scanf("%d%d", &qpos, &qk);
else scanf("%d%d%d", &ql, &qr, &qk);
if(opt == 1) printf("%d\n", Seg.getRank(1, ql, qr, qk) + 1);
else if(opt == 2) printf("%d\n", Seg.getKth(ql, qr, qk));
else if(opt == 3) Seg.modify(1, qpos, qk), a[qpos] = qk;
else if(opt == 4) printf("%d\n", Seg.getPre(1, ql, qr, qk));
else if(opt == 5) printf("%d\n", Seg.getSub(1, ql, qr, qk));
}
return 0;
}
二、線段樹套非旋Treap
同上。
Code#6
#include<cstdio>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
#define mid ((A[id].l + A[id].r) >> 1)
using namespace std;
const int INF = 0x7fffffff;
const int N = 50005;
int n, m, opt, ql, qr, qk, qpos, a[N];
struct Treap{
int val, son[2], size, hp;
}B[(int)4e6];
struct segTree{
int l, r, root;
}A[N<<2];
struct OPT_Treap{
int cnt;
inline int newNode(int val){
cnt++;
B[cnt].val = val;
B[cnt].son[0] = B[cnt].son[1] = 0;
B[cnt].size = 1;
B[cnt].hp = rand();
return cnt;
}
inline void pushup(int id){
if(!id) return;
B[id].size = 1;
if(B[id].son[0]) B[id].size += B[B[id].son[0]].size;
if(B[id].son[1]) B[id].size += B[B[id].son[1]].size;
}
int merge(int a, int b){
if(a == 0) return b;
if(b == 0) return a;
if(B[a].hp <= B[b].hp){
B[a].son[1] = merge(B[a].son[1], b);
pushup(a);
return a;
}
else{
B[b].son[0] = merge(a, B[b].son[0]);
pushup(b);
return b;
}
}
void split(int id, int k, int &x, int &y){
if(!id){
x = 0, y = 0;
return;
}
if(B[id].val > k)
y = id, split(B[id].son[0], k, x, B[id].son[0]);
else
x = id, split(B[id].son[1], k, B[id].son[1], y);
pushup(id);
}
inline void insert(int &rt, int val){
int l = 0, r = 0;
split(rt, val, l, r);
int t = newNode(val);
rt = merge(merge(l, t), r);
}
inline void del(int &rt, int val){
int l = 0, r = 0, t = 0;
split(rt, val - 1, l, t);
split(t, val, t, r);
t = merge(B[t].son[0], B[t].son[1]);
rt = merge(merge(l, t), r);
}
inline int getRank(int &rt, int x){
int l = 0, r = 0;
split(rt, x - 1, l, r);
int ans = B[l].size + 1;
rt = merge(l, r);
return ans;
}
inline int getPre(int &rt, int x){
int now = rt, ans = -INF;
while(now){
if(B[now].val < x){
ans = max(ans, B[now].val);
now = B[now].son[1];
}
else now = B[now].son[0];
}
return ans;
}
inline int getSub(int &rt, int x){
int now = rt, ans = INF;
while(now){
if(B[now].val > x){
ans = min(ans, B[now].val);
now = B[now].son[0];
}
else now = B[now].son[1];
}
return ans;
}
}BST;
struct OPT_segTree{
void build(int id, int l, int r){
A[id].l = l, A[id].r = r;
A[id].root = BST.newNode(INF);
if(A[id].l == A[id].r) return;
build(lid, l, mid);
build(rid, mid+1, r);
}
void insert(int id, int pos, int val){
BST.insert(A[id].root, val);
if(A[id].l == A[id].r) return;
if(pos <= mid) insert(lid, pos, val);
else insert(rid, pos, val);
}
void modify(int id, int pos, int val){
BST.del(A[id].root, a[pos]);
BST.insert(A[id].root, val);
if(A[id].l == A[id].r) return;
if(pos <= mid) modify(lid, pos, val);
else modify(rid, pos, val);
}
int query(int id, int l, int r, int x, int kind){
if(A[id].l == l && A[id].r == r){
if(kind == 0) return BST.getRank(A[id].root, x);
if(kind == 1) return BST.getPre(A[id].root, x);
if(kind == 2) return BST.getSub(A[id].root, x);
}
if(r <= mid) return query(lid, l, r, x, kind);
else if(l > mid) return query(rid, l, r, x, kind);
else{
if(kind == 0) return query(lid, l, mid, x, kind) + query(rid, mid+1, r, x, kind) - 1;
if(kind == 1) return max(query(lid, l, mid, x, kind), query(rid, mid+1, r, x, kind));
if(kind == 2) return min(query(lid, l, mid, x, kind), query(rid, mid+1, r, x, kind));
}
}
int getKth(int l, int r, int k){
int L = 0, R = 1e8, ans = 0;
while(L <= R){
int Mid = (L + R) >> 1;
int t1 = query(1, l, r, Mid, 0);
int t2 = query(1, l, r, Mid+1, 0) - 1;
if(t1 <= k && k <= t2){ ans = Mid; break; }
else if(t2 < k) L = Mid + 1;
else if(t1 > k) R = Mid - 1;
}
return ans;
}
}Seg;
int main(){
srand(200127);
scanf("%d%d", &n, &m);
BST.cnt = 0;
Seg.build(1, 1, n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
Seg.insert(1, i, a[i]);
}
while(m--){
scanf("%d", &opt);
if(opt == 3) scanf("%d%d", &qpos, &qk);
else scanf("%d%d%d", &ql, &qr, &qk);
if(opt == 1) printf("%d\n", Seg.query(1, ql, qr, qk, 0));
else if(opt == 2) printf("%d\n", Seg.getKth(ql, qr, qk));
else if(opt == 3) Seg.modify(1, qpos, qk), a[qpos] = qk;
else if(opt == 4) printf("%d\n", Seg.query(1, ql, qr, qk, 1));
else if(opt == 5) printf("%d\n", Seg.query(1, ql, qr, qk, 2));
}
return 0;
}
三、樹狀數組套值域線段樹(帶修改主席樹)
hmm…這道題其實可以不用平衡樹做,因爲要求第k大,自然而然想到主席樹可以做到,但這道題有修改操作,普通的維護前綴和的主席樹修改一次就要把後面所有樹都改了,所以修改一次的時間複雜度就是 的,顯然不行。於是,帶修改主席樹應運而生:我們不再讓值域線段樹們維護前綴和了,而是讓它們維護樹狀數組上對應的約 個點,這樣一次修改的時間複雜度就降到了 。
- 查詢區間內k的排名:相當於找比k小的數有多少個(答案是個數+1)。在值域線段樹上二分查找k時,如果往右兒子走,就把左兒子大小加進答案裏去就行了;
- 查詢區間內排名爲k的值:找到樹狀數組裏面相關的值域線段樹(存進一個數組,見代碼中的A[]和B[]),算出當前點左兒子大小,再決定是向左還是向右二分下去;
- 修改某位置的值:修改即先刪除原值,再插入新值;找到樹狀數組裏面相關的值域線段樹,對每棵樹都進行刪除和插入操作;
- 查詢k在區間內的前驅:查詢區間內比k小的數有多少個,如果沒有,輸出-INF;否則輸出區間內相應排名的值;
- 查詢k在區間內的後繼:查詢區間內比k大的數有多少個,如果沒有,輸出INF;否則輸出區間內相應排名的值。
涉及到值域線段樹一般都要離散化,以保證空間;同時,這道題還必須動態開點才能保證空間。
Code#7
紀念我的第一份超過200行的代碼…
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 0x7fffffff;
const int N = 50005;
int n, m, a[N], t[N<<1], f[N<<1], MX, A[20], B[20];//因爲有詢問操作,t[]和f[]空間一定要開夠!
int root[N], cnt;
struct Query{
int opt, l, r, k, pos;
}q[N];
struct segTree{
int size, son[2];
}tr[N*15*15];
inline void readin(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), t[++t[0]] = a[i];
for(int i = 1; i <= m; i++){
scanf("%d", &q[i].opt);
if(q[i].opt != 3){
scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].k);
if(q[i].opt != 2) t[++t[0]] = q[i].k;
}
else{
scanf("%d%d", &q[i].pos, &q[i].k);
t[++t[0]] = q[i].k;
}
}
}
inline void disc(){
sort(t+1, t+t[0]+1);
int len = unique(t+1, t+t[0]+1) - (t+1);
for(int i = 1; i <= n; i++){
int temp = lower_bound(t+1, t+len+1, a[i]) - t;
f[temp] = a[i], a[i] = temp;
MX = max(MX, temp);
}
for(int i = 1; i <= m; i++){
if(q[i].opt == 2) continue;
int temp = lower_bound(t+1, t+len+1, q[i].k) - t;
f[temp] = q[i].k, q[i].k = temp;
MX = max(MX, temp);
}
f[MX+1] = -INF;
f[MX+2] = INF;
}
inline int lowbit(int x){ return x & -x; }
inline void init1(int x, int X[]){
X[0] = 0;
for(int i = x; i; i -= lowbit(i)){
if(!root[i]) root[i] = ++cnt;
X[++X[0]] = root[i];
}
}
inline void init2(int x, int X[]){
X[0] = 0;
for(int i = x; i <= n; i += lowbit(i)){
if(!root[i]) root[i] = ++cnt;
X[++X[0]] = root[i];
}
}
inline void pushup(int id){
tr[id].size = tr[tr[id].son[0]].size + tr[tr[id].son[1]].size;
}
void insert(int &id, int l, int r, int val){
if(!id) id = ++cnt;
if(l == r){
tr[id].size++;
return;
}
int mid = (l + r) >> 1;
if(val <= mid) insert(tr[id].son[0], l, mid, val);
else insert(tr[id].son[1], mid+1, r, val);
pushup(id);
}
void del(int &id, int l, int r, int val){
if(!id) id = ++cnt;
if(l == r){
if(tr[id].size > 0) tr[id].size--;
return;
}
int mid = (l + r) >> 1;
if(val <= mid) del(tr[id].son[0], l, mid, val);
else del(tr[id].son[1], mid+1, r, val);
pushup(id);
}
int getSmaller(int l, int r, int k){
if(l == r) return 0;
int mid = (l + r) >> 1;
if(k <= mid){
for(int i = 1; i <= A[0]; i++){
if(!tr[A[i]].son[0]) tr[A[i]].son[0] = ++cnt;
A[i] = tr[A[i]].son[0];
}
for(int i = 1; i <= B[0]; i++){
if(!tr[B[i]].son[0]) tr[B[i]].son[0] = ++cnt;
B[i] = tr[B[i]].son[0];
}
return getSmaller(l, mid, k);
}
else{
int res = 0;
for(int i = 1; i <= A[0]; i++){
if(!tr[A[i]].son[1]) tr[A[i]].son[1] = ++cnt;
res -= tr[tr[A[i]].son[0]].size;
A[i] = tr[A[i]].son[1];
}
for(int i = 1; i <= B[0]; i++){
if(!tr[B[i]].son[1]) tr[B[i]].son[1] = ++cnt;
res += tr[tr[B[i]].son[0]].size;
B[i] = tr[B[i]].son[1];
}
return res + getSmaller(mid+1, r, k);
}
}
int getBigger(int l, int r, int k){
if(l == r) return 0;
int mid = (l + r) >> 1;
if(k <= mid){
int res = 0;
for(int i = 1; i <= A[0]; i++){
if(!tr[A[i]].son[0]) tr[A[i]].son[0] = ++cnt;
res -= tr[tr[A[i]].son[1]].size;
A[i] = tr[A[i]].son[0];
}
for(int i = 1; i <= B[0]; i++){
if(!tr[B[i]].son[0]) tr[B[i]].son[0] = ++cnt;
res += tr[tr[B[i]].son[1]].size;
B[i] = tr[B[i]].son[0];
}
return res + getBigger(l, mid, k);
}
else{
for(int i = 1; i <= A[0]; i++){
if(!tr[A[i]].son[1]) tr[A[i]].son[1] = ++cnt;
A[i] = tr[A[i]].son[1];
}
for(int i = 1; i <= B[0]; i++){
if(!tr[B[i]].son[1]) tr[B[i]].son[1] = ++cnt;
B[i] = tr[B[i]].son[1];
}
return getBigger(mid+1, r, k);
}
}
int getKth(int l, int r, int k){
if(l == r) return l;
int lsize = 0;
for(int i = 1; i <= A[0]; i++) lsize -= tr[tr[A[i]].son[0]].size;
for(int i = 1; i <= B[0]; i++) lsize += tr[tr[B[i]].son[0]].size;
int mid = (l + r) >> 1;
if(lsize >= k){
for(int i = 1; i <= A[0]; i++){
if(!tr[A[i]].son[0]) tr[A[i]].son[0] = ++cnt;
A[i] = tr[A[i]].son[0];
}
for(int i = 1; i <= B[0]; i++){
if(!tr[B[i]].son[0]) tr[B[i]].son[0] = ++cnt;
B[i] = tr[B[i]].son[0];
}
return getKth(l, mid, k);
}
else{
for(int i = 1; i <= A[0]; i++){
if(!tr[A[i]].son[1]) tr[A[i]].son[1] = ++cnt;
A[i] = tr[A[i]].son[1];
}
for(int i = 1; i <= B[0]; i++){
if(!tr[B[i]].son[1]) tr[B[i]].son[1] = ++cnt;
B[i] = tr[B[i]].son[1];
}
return getKth(mid+1, r, k - lsize);
}
}
inline int getPre(int ql, int qr, int k){
init1(ql-1, A), init1(qr, B);
int rank = getSmaller(1, MX, k) + 1;
init1(ql-1, A), init1(qr, B);
if(rank == 1) return MX+1;
else return getKth(1, MX, rank-1);
}
inline int getSub(int ql, int qr, int k){
init1(ql-1, A), init1(qr, B);
int rank = getBigger(1, MX, k) + 1;
init1(ql-1, A), init1(qr, B);
if(rank == 1) return MX+2;
else return getKth(1, MX, qr - ql + 3 - rank);
}
int main(){
readin();
disc();
for(int i = 1; i <= n; i++){
init2(i, A);
for(int j = 1; j <= A[0]; j++)
insert(A[j], 1, MX, a[i]);
}
for(int i = 1; i <= m; i++){
switch(q[i].opt){
case 1: init1(q[i].l-1, A); init1(q[i].r, B); printf("%d\n", getSmaller(1, MX, q[i].k) + 1); break;
case 2: init1(q[i].l-1, A); init1(q[i].r, B); printf("%d\n", f[getKth(1, MX, q[i].k)]); break;
case 3:{
init2(q[i].pos, A);
for(int j = 1; j <= A[0]; j++){
del(A[j], 1, MX, a[q[i].pos]);
insert(A[j], 1, MX, q[i].k);
}
a[q[i].pos] = q[i].k;
break;
}
case 4: printf("%d\n", f[getPre(q[i].l, q[i].r, q[i].k)]); break;
case 5: printf("%d\n", f[getSub(q[i].l, q[i].r, q[i].k)]); break;
}
}
return 0;
}