主席樹這個概念應該不陌生吧!恩?不會, 戳這裏。php
主席樹(函數式線段樹)用的是函數思想,一個節點開數組用來保存本身的左右節點,這樣節省許多沒必要要的空間,還能夠保存許多歷史狀態。而這裏咱們用的是主席樹的函數思想來實現。html
上題:http://acm.hdu.edu.cn/showproblem.php?pid=5444數組
題目大意:函數
給你一個序列,第一個數爲二叉樹根節點,以後每一個數往上加節點,且保證左節點小於根節點,且保證右節點大於根節點。且每一個節點最多有2個子節點。而後再查詢位置,每往左找輸出一個E,右找輸出W。例如序列2, 1, 4, 3能夠生成以下圖:ui
例如查找1,須要往左一次輸出E,查找2,不須要搜直接輸出,查找3須要向右一次再向左一次,輸出WE。spa
哇!這題好水,不就是二叉樹嗎?啪啪啪,幾分鐘碼完了, 交一發,嗯,竟然RE了,不行,的開大叔組,開成10W,嗯?又RE了。最後一想若是這個數列是1-n,即a[i] = i,那樣須要訪問到2的1000次方個節點。咕~~(╯﹏╰)b,鬱悶呢,而後回想起之前學過的主席樹,能夠開數組記錄該節點的左右兒子,那樣豈不是隻要訪問到n個節點就行。而後就這樣AC了。(比賽的時候提交的時候超了了51s,,14:00:51的時候提交的。原本能AC的,TAT)htm
附上代碼:blog
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 4000 + 5; int a[N], b[N],ls[N], rs[N], mx[N]; int n, k, tot, sz, ql, qr, x, q, T; void update(int o, int l, int r, int p){ int m = (l + r) >> 1; if(p <= mx[o]){ if(ls[o] == 0){ ls[o] = tot; mx[tot] = p; return ; } else update(ls[o], l, m, p); } else { if(rs[o] == 0){ rs[o] = tot; mx[tot] = p; return; } else update(rs[o], m + 1, r, p); } } void query(int o, int l, int r, int k){ if(mx[o] == k)return ; int m = (l + r) >> 1; if(k <= mx[o]){ printf("E"); query(ls[o], l, m, k); } else{ printf("W"); query(rs[o], m + 1, r, k); } } void work(){ scanf("%d", &x); query(1, 1, n, x); puts(""); } int main(){ scanf("%d", &T); while(T--){ scanf("%d", &n); tot = 1; //Build(rt[0], 1, n); memset(mx, 0, sizeof(mx)); memset(ls, 0, sizeof(ls)); memset(rs, 0, sizeof(rs)); //for(int i = 1; i <= n; i ++)ls[i] = i << 1, rs[i] = i << 1|1; //for(int i = 0; i <= 20; i ++)printf("i = %d, rt = %d, ls = %d, rs= %d, mx = %d\n", i, rt[i], ls[i], rs[i], mx[i]); for(int i = 1; i <= n; i ++){ scanf("%d", a + i); if(i == 1)mx[1] = a[1]; else update(1, 1, n, a[i]); tot ++; } scanf("%d", &q); while(q --)work(); } return 0; }