1. 程式人生 > >[HNOI2017]單旋

[HNOI2017]單旋

sse lol line 最大 freopen gin rgs 一個點 digi

題意:

模擬一棵單旋splay,支持五種操作

技術分享圖片

\(\text{Solution:}\)

顯然不是讓你碼一顆單旋splay(一條鏈卡爆你)。

在草稿紙上畫一畫,模擬一遍,觀察中序遍歷下的深度變化,發現當x為最小值時它沒有左兒子,可能有右兒子,splay到根後除了它的右兒子深度不變,其余的深度都+1,x為最大值時同理。

因此,每次操作我們都可以用線段樹直接維護深度,接下來要解決的問題就是怎麽插入。

二叉搜索樹一個點總是插在其前驅的右兒子或是後繼的左兒子

所以我們只要找到它前驅或後繼,然後接在它的下面,這個過程很容易用set實現,還要記錄每個點的左兒子以及右兒子是誰,不然無法判斷插入到前驅和後繼哪個下面去。

\(\text{Source}\)

// luogu-judger-enable-o2
#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <assert.h>
#include <algorithm>

using namespace std;

#define LL long long
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO debug("GO\n")

inline int rint() {
  register int x = 0, f = 1; register char c;
  while (!isdigit(c = getchar())) if (c == '-') f = -1;
  while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getchar()));
  return x * f;
}

template<typename T> inline void chkmin(T &a, T b) { a > b ? a = b : 0; }
template<typename T> inline void chkmax(T &a, T b) { a < b ? a = b : 0; }

const int N = 1e5 + 10;

#define Iter set<int>::iterator
set<int> S;
int a[N], tot, ch[N][2], fa[N], m, op[N], buc[N], root; int dep[N<<2];

void add(int x, int l, int r, int L, int R, int val) {
  if (L <= l and r <= R) {
    dep[x] += val;
    return;
  }
  int mid = (l + r) >> 1;
  if (L <= mid) add(x<<1, l, mid, L, R, val);
  if (mid < R) add(x<<1|1, mid + 1, r, L, R, val);
}

int query(int x, int l, int r, int p) {
  if (l == r) {
    return dep[x];
  }
  int mid = (l + r) >> 1;
  if (p <= mid) return dep[x] + query(x<<1, l, mid, p);
  else return dep[x] + query(x<<1|1, mid+1, r, p);
}

void change(int x, int y) {
  add(1, 1, tot, x, x, y - query(1, 1, tot, x));
}

int Insert(int x) {
  Iter it = S.insert(x).first;
  if (root == 0) {
    root = x;
    change(x, 1);
    return 1;
  }
  if (it != S.begin()) {
    if (ch[*--it][1] == 0) ch[fa[x] = *it][1] = x;
    it++;
  }
  if (fa[x] == 0) ch[fa[x] = *++it][0] = x;
  int res = query(1, 1, tot, fa[x]) + 1;
  change(x, res);
  return res;
}

int findmin() {
  int x = *S.begin(), res = query(1, 1, tot, x);
  if (x == root) return 1;
  if (x + 1 <= fa[x] - 1)
    add(1, 1, tot, x + 1, fa[x] - 1, -1);
  add(1, 1, tot, 1, tot, 1);
  ch[fa[x]][0] = ch[x][1], fa[ch[x][1]] = fa[x];
  fa[root] = x, ch[x][1] = root;
  root = x;
  change(x, 1);
  return res;
}

int findmax() {
  int x = *S.rbegin(), res = query(1, 1, tot, x);
  if (x == root) return 1;
  if (x - 1 >= fa[x] + 1)
    add(1, 1, tot, fa[x] + 1, x - 1, -1);
  add(1, 1, tot, 1, tot, 1);
  ch[fa[x]][1] = ch[x][0], fa[ch[x][0]] = fa[x];
  fa[root] = x, ch[x][0] = root;
  root = x;
  change(x, 1);
  return res;
}

void delmin() {
  printf("%d\n", findmin());
  add(1, 1, tot, 1, tot, -1);
  S.erase(root);
  root = ch[root][1];
  fa[root] = 0;
}

void delmax() {
  printf("%d\n", findmax());
  add(1, 1, tot, 1, tot, -1);
  S.erase(root);
  root = ch[root][0];
  fa[root] = 0;
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("xhc.in", "r", stdin);
  freopen("xhc.out", "w", stdout);
#endif
  m = rint();
  for (int i = 1; i <= m; ++ i) {
    op[i] = rint();
    if (op[i] == 1) {
      tot++;
      a[tot] = buc[tot] = rint();
    }
  }
  sort(buc + 1, buc + 1 + tot);
  for (int i = 1; i <= tot; ++ i)
    a[i] = lower_bound(buc + 1, buc + 1 + tot, a[i]) - buc;
  for (int i = 1, cnt = 0; i <= m; ++ i) {
    switch(op[i]) {
    case 1: printf("%d\n", Insert(a[++cnt])); break;
    case 2: printf("%d\n", findmin()); break;
    case 3: printf("%d\n", findmax()); break;
    case 4: delmin(); break;
    case 5: delmax(); break;
    }
  }
}

[HNOI2017]單旋