luogu3380 樹套樹之線段樹套線段樹
阿新 • • 發佈:2019-02-03
range spa cst while 所有 權值線段樹 線段 普通 change
個人感覺可能是最不需要腦子寫的方法
不過也不太好調
就是用一個普通的線段樹維護這個序列,但是對於線段樹的每一個區間,再開一個動態開點的權值線段樹,裏面存儲這個區間所有元素值
單點修改只會涉及到log棵權值線段樹的單點修改(不用打lazy太棒了 log^2
查詢區間內x的排名相當於查詢區間內<x的數的個數+1,我們把區間分成log個外層線段樹上的區間,然後在每個權值線段樹上統計即可,復雜度log^2
查詢排名為x的數比較麻煩,我們直接二分,復雜度log^3
查詢前驅後繼:由於線段樹維護的區間,總區間是把這log個區間相加,所以我們再每個權值線段樹查詢下前驅後繼再合並就行,前驅取max,後繼取min
至於怎麽查詢,可以在線段樹上二分
代碼寫的特別亂...
#include <cstdio> #include <functional> using namespace std; int l[17000010], r[17000010], tree[17000010], tot; int rt[200010], init[50010], fuck = 100000000; int s[10000010], top; int n, m; void chenge(int &x, int cl, int cr, int pos, int val) { if (x == 0) { if (top > 0) x = s[top--]; else x = ++tot; } if (tot % 100000 == 0) fprintf(stderr, "(%d, %d)\n", tot, top); if (cl == cr) {tree[x] += val; if (tree[x] == 0) s[++top] = x, x = 0; return; } int mid = (cl + cr) / 2; if (pos > mid) chenge(r[x], mid + 1, cr, pos, val); else chenge(l[x], cl, mid, pos, val); tree[x] = tree[l[x]] + tree[r[x]]; if (l[x] == 0 && r[x] == 0) { s[++top] = x, x = 0; } } int query(int x, int cl, int cr, int pos) { if (x == 0 || cl == cr) return 0; int mid = (cl + cr) / 2; if (pos > mid) return tree[l[x]] + query(r[x], mid + 1, cr, pos); else return query(l[x], cl, mid, pos); } int qrange(int x, int cl, int cr, int L, int R) { if (x == 0) return 0; if (R < cl || cr < L) return 0; if (L <= cl && cr <= R) return tree[x]; int mid = (cl + cr) / 2; return qrange(l[x], cl, mid, L, R) + qrange(r[x], mid + 1, cr, L, R); } int getnumber(int x, int cl, int cr, int rank) { if (cl == cr) { return cl; } int mid = (cl + cr) / 2; if (rank <= tree[l[x]]) return getnumber(l[x], cl, mid, rank); else return getnumber(r[x], mid + 1, cr, rank - tree[l[x]]); } int getnumber2(int x, int cl, int cr, int rank) { if (cl == cr) { return cl; } int mid = (cl + cr) / 2; if (rank <= tree[r[x]]) return getnumber2(r[x], mid + 1, cr, rank); else return getnumber2(l[x], cl, mid, rank - tree[r[x]]); } int getprev(int rt, int pos) { int tot = qrange(rt, 0, fuck, 0, pos - 1); // [1, pos - 1]內數的個數 if (tot == 0) return -2147483647; return getnumber(rt, 0, fuck, tot); } int getnext(int rt, int pos) { int tot = qrange(rt, 0, fuck, pos + 1, fuck); if (tot == 0) return 2147483647; return getnumber2(rt, 0, fuck, tot); } //---- 外面線段樹 void build(int x, int l, int r) { for (int i = l; i <= r; i++) chenge(rt[x], 0, fuck, init[i], 1); if (l == r) return; int mid = (l + r) / 2; build(x * 2, l, mid); build(x * 2 + 1, mid + 1, r); } int qrank(int x, int cl, int cr, int L, int R, int k) { if (R < cl || cr < L) return 0; if (L <= cl && cr <= R) return query(rt[x], 0, fuck, k); int mid = (cl + cr) / 2; return qrank(x * 2, cl, mid, L, R, k) + qrank(x * 2 + 1, mid + 1, cr, L, R, k); } void change(int x, int cl, int cr, int pos, int val) { chenge(rt[x], 0, fuck, init[pos], -1); chenge(rt[x], 0, fuck, val, 1); if (cl == cr) return; int mid = (cl + cr) / 2; if (pos > mid) change(x * 2 + 1, mid + 1, cr, pos, val); else change(x * 2, cl, mid, pos, val); } int qprev(int x, int cl, int cr, int L, int R, int k) { if (R < cl || cr < L) return -2147483647; if (L <= cl && cr <= R) { int res = getprev(rt[x], k); return res; } int mid = (cl + cr) / 2; return max(qprev(x * 2, cl, mid, L, R, k), qprev(x * 2 + 1, mid + 1, cr, L, R, k)); } int qnext(int x, int cl, int cr, int L, int R, int k) { if (R < cl || cr < L) return 2147483647; if (L <= cl && cr <= R) return getnext(rt[x], k); int mid = (cl + cr) / 2; return min(qnext(x * 2, cl, mid, L, R, k), qnext(x * 2 + 1, mid + 1, cr, L, R, k)); } int main() { // printf("%f\n", (3 * sizeof(l) + sizeof(s) + sizeof(rt)) / 1000000.); scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &init[i]); build(1, 1, n); for (int opd, l, r, k, i = 1; i <= m; i++) { scanf("%d%d%d", &opd, &l, &r); if (opd != 3) scanf("%d", &k); if (opd == 1) { printf("%d\n", qrank(1, 1, n, l, r, k) + 1); } if (opd == 2) // query值<k的最大一撮數中最小的一個 { // for (int i = 0; i <= 10; i++) printf("query(%d) = %d\n", i, qrank(1, 1, n, l, r, i)); int cl = 0, cr = 100000000; while (cl < cr) { int mid = (cl + cr + 1) / 2; if (qrank(1, 1, n, l, r, mid) < k) cl = mid; else cr = mid - 1; } // int ans = qrank(1, 1, n, l, r, cl); // cl = 0, cr = 100000000; // while (cl < cr) // { // int mid = (cl + cr) / 2; // if (qrank(1, 1, n, l, r, mid) >= ans) cr = mid; // else cl = mid + 1; // } printf("%d\n", cl); } if (opd == 3) { change(1, 1, n, l, r); init[l] = r; } if (opd == 4) { printf("%d\n", qprev(1, 1, n, l, r, k)); } if (opd == 5) { printf("%d\n", qnext(1, 1, n, l, r, k)); } } return 0; }
luogu3380 樹套樹之線段樹套線段樹