資料結構們

小複習

區間最大連續和

spoj GSS3

序列操作題

codeforces ???

  1. l,r,x,dl, r, x, d : 把區間 [l,r][l, r] 加上首項為 xx 公差為 dd 的序列
  2. l,rl, r : 詢問區間和

矩形覆蓋面積計算

tioj 1224

二元搜尋樹 

 

Naïve

binary search tree

  1. O(n)\mathcal{O}(n) insert xx :
    順著找到可以放的位置
  2. O(n)\mathcal{O}(n) erase xx :
    找到它後拔掉 找到子樹中的 lower_bound( xx ) ,然後把它換上來
  3. O(n)\mathcal{O}(n) query xx:
    在樹上(搞不好是鍊)二分(所以可能不算二分搜)搜

Tree + Heap = Treap

Treap 

  1. O(log2n)\mathcal{O}(log_2 n) insert xx :
    ????
  2. O(log2n)\mathcal{O}(log_2 n) erase xx :
    ????
  3. O(log2n)\mathcal{O}(log_2 n) query xx:
    二分搜

Merge Split Treap

  1. Merge :
    把一個 key 全部都小於等於 kk 的 Treap
    和一個 key 全部都大於等於 kk 的 Treap
    合併起來
  2. Split :
    把一棵 Treap 分割成
    一個 key 全部都小於         kk 的 Treap 和
    一個 key 全部都大於等於 kk 的 Treap
  1. O(log2n)\mathcal{O}(log_2 n) insert xx :
    把自己 split 成 key < xx 和 key x\geq x 的兩塊 a,ba, b
    然後合併 a,x,ba, x, b
  2. O(log2n)\mathcal{O}(log_2 n) erase xx :
    把自己 split 成 key < xx 和 key x\geq x 的兩塊 a,ba, b
    再把 bb split 成 key x\leq x 和 key > xx 的兩塊 c,dc, d
    然後合併 a,c,da, c, d
  3. O(log2n)\mathcal{O}(log_2 n) query xx:
    二分搜

Merge Split Treap

Operation 1 : Merge

key 3\leq 3

key 3\geq 3

比較 pri

?

比較 pri

比較 pri

比較 pri

比較 pri

比較 pri

比較 pri

Operation 2 : Split

k=4k = 4

k=4k = 4

k=4k = 4

k=4k = 4

k=4k = 4

實做

 

unsigned ran() {
    static unsigned x = 19;
    return ++(x *= 0xdefaced);
}
struct node {
    node * left = NULL, * right = NULL;
    int key;
    unsigned pri;
    node(int key):key(key), pri(ran()){};
};

實做

 

node * merge (node* a, node* b) {
    if (!a)return b;
    if (!b)return a;
    if (a->pri < b->pri) {
        a->right = merge(a->right, b);
        return a;
    }
    else {
        b->left = merge(a, b->left);
        return b;
    }
}

實做

 

void split(node* now, int key, node* &a, node* &b) {
    if (!now) {
        a = b = NULL;
        return;
    }
    if (now->key < key) {
        a = now;
        split(now->right, key, a->right, b);
    }
    else {
        b = now;
        split(now->left, key, a, b->left);
    }
}

關於 treap 小技巧

  1. 其實 pri 值只有用在 merge 決定上下的時候
    所以其實可以在那個時候 再決定就好
    可以發現若兩棵樹的大小分別是 Sa,SbS_a, S_b,那 麼 aa 應該要在上層的機率約是 SaSa+Sb\frac{S_a}{S_a + S_b}
  2. 如果順便維護子樹的大小,那就可以在樹上二分搜求出第 kk 大的元素
  3. 在序列上,把 index 視為 key,則可以建出一個完整序列的 Treap,查詢區間時,只要把整格區間切下來並且維護子樹的資訊就好。可以做到線段樹支援的事情(只是比較慢....

TNFSHOJ 31
大榕樹的祈禱

維護一個序列,操作有以下

  1. insert p k v1,v2,v3...vk\text{insert}  p  k  v_1, v_2, v_3... v_k:
    kk 個數字v1,v2,v3...vkv_1, v_2, v_3...v_k插入在第 pp 個數字的後面
  2. delete p k\text{delete}  p  k :
    從第 pp 個數字開始連續刪除 kk 個數字
  3. make-same p k v\text{make-same}  p  k  v : pp 開始的連續 kk 數字都改成 vv
  4. reverse p k\text{reverse}  p  k : pp 開始的連續 kk 個數字倒轉
  5. get-sum p k\text{get-sum}  p  k : 計算從 pp 開始的 kk 個數字的和
  6. max-sum\text{max-sum} : 計算目前整個數列中最大的連續和

小例題

請維護一個集合,使得他支援兩種操作 :

  1. 加入一個數字 xx
  2. 詢問目前第 kk 大的數字

x105x \leq 10^5

操作數量 105\leq 10^5

先假設線段樹不是代表一個序列,而是值域?

小例題

請維護一個集合,使得他支援兩種操作 :

ojdl 7129

  1. 加入一個數字 xx
  2. 詢問目前第 kk 大的數字

# : x109x \leq 10^9

操作數量 3105\leq 3 \cdot10^5

需要再開!

持久化

隕石

有一些城市 編號由 1 n1 ~ n

有一些科學家想採集隕石樣本,而第 ii 個城市需要採集 pip_i

kk 筆事件兩兩事件時間差為 1 且按照時間順序 li,ri,ail_i, r_i, a_i 表達在從 [li,ri][l_i, r_i] 都將會有 aia_i 顆隕石墜落

請輸出 nn 行代表在第 ii 座城市至少要多久才能完成樣本採集 

 

1n300000,1k3000001 \leq n \leq 300000, 1 \leq k \leq 300000

 

 

如果我有 nn 顆線段樹

ii 顆線段樹紀錄了時間從 [1,i][1, i] 的所有事件,

那就對於每個 ii 就可以二分搜答案了!

持久化線段樹

 

 

實作

 

struct node {
    node *left = NULL, *right = NULL;
    int val;
};

實做

 

node* add(node* old, int pos, int val) {
    node* res = new node(old), * now = res;
    int l = 1, r = n, m;
    res->val += val;
    while (l < r) {
        m = l + r >> 1;
        if (pos > m) {
            now->right = new node(old->right);
            now->right->val += val;
            now = now->right;
            old = old->right;
            r = m;
        }
        else {
            now->left = new node(old->left);
            now->left->val += val;
            now = now->left;
            old = old->left;
            l = m+1;
        }
    }
    return res;
}

實作

 

node* add(node* old, int pos, int val) {
    node* res = new node(old), * now = res;
    int l = 1, r = n, m;
    res->val += val;
    while (l < r) {
        m = l + r >> 1;
        if (pos > m) {
            now->right = new node(old->right);
            now->right->val += val;
            now = now->right;
            old = old->right;
            r = m;
        }
        else {
            now->left = new node(old->left);
            now->left->val += val;
            now = now->left;
            old = old->left;
            l = m+1;
        }
    }
    return res;
}

例題

POJ2104

靜態區間第 K K

TIOJ 1975

有兩種工作,總共有 n 個

第一種會佔用一個處理器 使用從 lil_irir_i 的所有時間 並且從頭到尾只能使用同一個處理器

第二種有完成期限,可以分配給不同的處理器,必須在 did_i之前完成,需要花 wiw_i 的時間

請問至少需要幾個處理器 才能完成所有工作

NOTE : 一個處理器在任何時刻都只能處理一個工作
第二種工作在任何時刻也最多只能被一個處理器處理

li ri106l_i \leq\ r_i \leq 10^6

n105n \leq 10^5

 

TIOJ 1840

帶修改區間 KK

BIT +

動態開點值域線段樹

struct bit {
    node* root[maxn];
    void add(int x, int y, int val) {
        for (;x <= maxn;x += x & -x)
            modify(root[x], y, val);
    }
    
};

struct bit{    
    void shift(vector<node*> &nodes, int dir) {
        static const l_type = 0;
        for (auto &i : nodes) i = (dir == l_type ? i->left : i->right);
    }
    int left_sum(vector<node*> &nodes) {
        int res = 0;
        for (auto i : nodes) res += i->left->cnt;
        return res;
    }
    int query(int l, int r, int k) {
        vector<node*> lnode, rnode;
        for (int i = l;i;i ^= i & -i) lnode.pb(root[i]);
        for (int i = r;i;i ^= i & -i) rnode.pb(root[i]);
        int L = 1, R = n, M;
        while (L < r) {
            M = L + R >> 1;
            if (int lcnt = left_sum(rnode) - left_sum(lnode) ; lcnt >= k) {
                shift(rnode, 0), shift(lnode, 0);
                R = M;
            }
            else {
                k -= lcnt;
                shift(rnode, 1), shift(lnode, 1);
                L = M+1;
            }
        }
        return L;
    }
};

最長遞增子序列 LIS

map 維護單調隊列

dpi:=dp_i := 以數值 ii 結尾最長的答案

用 map<int,int> dp;

q,(q105)q, (q \leq 10^5) 筆操作

  1. 加進一筆資料 x,y,w,(x105,y105,w109)x, y, w, (x \leq 10^5, y \leq 10^5, w \leq 10^9)
  2. 詢問 a,ba, b :
    輸出在所有資料中 xa,ybx \leq a, y \leq b 中最大的 ww

最後一題

在碼頭上,你擁有一個碼頭,根據政府規定,一個碼頭一天最多只能有兩艘船停靠卸貨,你手上有一份名單上面有

n105 n \leq 10^5 個資訊,告訴你今天所有船的

停靠時間,離開時間,以及獲利

li,ri,ci,(liri105,ci109)l_i, r_i, c_i, (l_i \leq r_i \leq 10^5, c_i \leq 10^9) 

在任意時刻,一個碼頭最多都只能停靠一艘船,你可以選擇要不要讓他停靠,請問你今天最多能夠獲利多少呢?

 

 

Made with Slides.com