luogu P3721 [AH2017/HNOI2017]單旋 線段樹
阿新 • • 發佈:2018-12-09
題意
- 給你一顆單旋的
樹,要求完成以下操作
1.插入一個節點
2.把最小值單旋到根
3.把最大值單旋到根
4.2操作後刪除最小值
5.3操作後刪除最大值
要求求出每次操作時操作點的深度
我覺得這題沒有那些題解裡講的那麼簡單啊…
首先看到這題第一個想法就是按題意模擬一顆 ,但這樣顯然會被卡,我們需要發掘一下操作的性質。首先插入節點,手玩幾次可以發現一定是插入在前驅後繼中深度較大的那個節點,並且前驅後繼是連在一起的。然後對於單旋操作,我們發現單旋最小值的時候只有左旋,單旋最大值的時候只有右旋,那麼整個樹父只有 個點父子關係發生了變化,並且旋轉完以後除了它的子樹內的點所有點的深度都加了 ,刪除的話把所有點深度減 就好了。那麼我們可以這樣子來考慮,用一個線段樹來維護點的深度,用一個 來維護平衡樹結構,那麼就可以做了。
因為點權互不相同,我們離散化後把點的編號當做權值,用一個 維護當前正在 內的所有點來快速查詢前驅後繼。
1操作我們找到前驅後繼後用線段樹查詢深度就好了
2操作我們首先查詢深度,再修改深度,然後修改父子關係,要修改根、最小值,最小值的父親,最小值的右子樹,模擬
的過程就好了。
3操作同上
4操作只要在2操作後先修改深度再修改根的父子關係就好了
5操作同上
複雜度
Codes
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct Que {
int x, y;
}Q[N];
int tmp[N], cnt, q;
struct Segment_Tree {
#define mid ((l + r) >> 1)
#define ls (bh << 1)
#define rs (ls | 1)
#define lson (ls), (l), (mid)
#define rson (rs), (mid + 1), (r)
int S[N << 2], lazy[N << 2];
void modify(int bh, int l, int r, int z) {
lazy[bh] += z, S[bh] += (r - l + 1) * z;
}
void pushup(int bh) {
S[bh] = S[ls] + S[rs];
}
void pushdown(int bh, int l, int r) {
if (lazy[bh]) {
modify(lson, lazy[bh]);
modify(rson, lazy[bh]);
lazy[bh] = 0;
}
}
void update(int bh, int l, int r, int x, int y, int z) {
if (x <= l && r <= y) modify(bh, l, r, z);
else {
pushdown(bh, l, r);
if (x <= mid) update(lson, x, y, z);
if (y > mid) update(rson, x, y, z);
pushup(bh);
}
}
int query(int bh, int l, int r, int x) {
if (l == r) return S[bh];
pushdown(bh, l, r);
if (x <= mid) return query(lson, x);
return query(rson, x);
}
void setval(int pos, int val) {
update(1, 1, cnt, pos, pos, val - query(1, 1, cnt, pos));
}
}T1;
struct Spaly_Tree {
set<int> S; set<int>::iterator Pre, Sub;
int root, fa[N], ch[N][2];
int newnode(int val, int dad) {
S.insert(val), fa[val] = dad, ch[dad][val > dad] = val;
T1.setval(val, T1.query(1, 1, cnt, dad) + 1);
return T1.query(1, 1, cnt, val);
}
int ins(int val) {
if (!root) return S.insert(root = val), T1.setval(val, 1), 1;
if (*S.begin() > val) return newnode(val, *S.begin());
if (*S.rbegin() < val) return newnode(val, *S.rbegin());
Pre = -- S.lower_bound(val), Sub = S.upper_bound(val);
int dep1 = T1.query(1, 1, cnt, *Pre), dep2 = T1.query(1, 1, cnt, *Sub);
return newnode(val, *(dep1 > dep2 ? Pre : Sub));
}
int splay_min() {
int val = *S.begin(), dep = T1.query(1, 1, cnt, val);
if (val == root) return 1;
T1.update(1, 1, cnt, 1, cnt, 1), T1.update(1, 1, cnt, val, fa[val] - 1, -1), T1.setval(val, 1);
fa[root] = val, ch[fa[val]][0] = ch[val][1], fa[ch[val][1]] = fa[val];
ch[val][1] = root, root = val, fa[root] = fa[0] = 0;
return dep;
}
int splay_max() {
int val = *S.rbegin(), dep = T1.query(1, 1, cnt, val);
if (val == root) return 1;
T1.update(1, 1, cnt, 1, cnt, 1), T1.update(1, 1, cnt, fa[val] + 1, val, -1), T1.setval(val, 1);
fa[root] = val, ch[fa[val]][1] = ch[val][0], fa[ch[val][0]] = fa[val];
ch[val][0] = root, root = val, fa[root] = fa[0] = 0;
return dep;
}
void erase_min() {
int val = root; S.erase(root);
root = ch[val][1], ch[val][1] = fa[root] = 0;
T1.update(1, 1, cnt, 1, cnt, -1);
}
void erase_max() {
int val = root; S.erase(root);
root = ch[val][0], ch[val][0] = fa[root] = 0;
T1.update(1, 1, cnt, 1, cnt, -1);
}
}T2;
int main() {
#ifdef ylsakioi
freopen("3721.in", "r", stdin);
freopen("3721.out", "w", stdout);
#endif
scanf("%d", &q);
for (int i = 1; i <= q; ++ i) {
scanf("%d", &Q[i].x);
if (Q[i].x == 1) {
scanf("%d", &Q[i].y);
tmp[++ cnt] = Q[i].y;
}
}
sort(tmp + 1, tmp + cnt + 1);
for (int i = 1; i <= q; ++ i) {
if (Q[i].x == 1) printf("%d\n", T2.ins(lower_bound(tmp + 1, tmp + cnt + 1, Q[i].y) - tmp));
if (Q[i].x == 2) printf("%d\n", T2.splay_min());
if (Q[i].x == 3) printf("%d\n", T2.splay_max());
if (Q[i].x == 4) printf("%d\n", T2.splay_min()), T2.erase_min();
if (Q[i].x == 5) printf("%d\n", T2.splay_max()), T2.erase_max();
}
return 0;
}