BZOJ3196 二逼平衡树 Solution

来源:互联网 发布:2016美国经济非农数据 编辑:程序博客网 时间:2024/05/21 11:27

题意:写一个数据结构支持如下操作:

(1)区间第k大

(2)区间内求某个数的排名

(3)修改某个位置的数

(4)区间内求某个数的前趋、后继。


Sol:以下提供两种做法。

Sol1:线段树套平衡树。非常裸的做法,除询问区间第k大复杂度为O(log^3n),其余操作时间复杂度为O(log^2n).

Code1:

#include<cstdio>#include<cstring>#include<cstdlib>#define INF (100000000)#define l(x) S[x].l#define r(x) S[x].r#define v(x) S[x].v#define cnt(x) S[x].cnt#define p(x) S[x].p#define s(x) S[x].s#define lson(x) Q[x].lson#define rson(x) Q[x].rson#define dl(x) Q[x].dl#define dr(x) Q[x].dr#define root(x) Q[x].rootconst int N = 50001;inline int _min(int a , int b){    return a < b ? a : b;}inline int _max(int a , int b){    return a > b ? a : b;}struct Node{    int l , r , v , cnt , p , s;}S[2000050];int Node_Ind;int newnode(int x){    int q = ++Node_Ind;    v(q) = x;    cnt(q) = 1;    p(q) = rand();    s(q) = 1;    return q;}void maintain(int &q){    s(q) = s(l(q)) + cnt(q) + s(r(q));}void lr(int &q){    int tmp = r(q);    r(q) = l(tmp);    l(tmp) = q;    maintain(q);    maintain(tmp);    q = tmp;}void rr(int &q){    int tmp = l(q);    l(q) = r(tmp);    r(tmp) = q;    maintain(q);    maintain(tmp);    q = tmp;}void insert(int x , int &q){    if (!q)        q = newnode(x);    else    {        if (x == v(q))            ++cnt(q);        else if (x < v(q))        {            insert(x , l(q));            if (p(q) < p(l(q)))                rr(q);        }        else        {            insert(x , r(q));            if (p(q) < p(r(q)))                lr(q);        }    }    maintain(q);}void remove(int x , int &q){    if (!q)        return;    if (x == v(q))    {        if (cnt(q) > 1)            --cnt(q);        else if (!l(q) || !r(q))            q = (!l(q)) ? r(q) : l(q);        else if (p(l(q)) < p(r(q)))        {            lr(q);            remove(x , l(q));        }        else        {            rr(q);            remove(x , r(q));        }    }    else if (x < v(q))        remove(x , l(q));    else        remove(x , r(q));    if (q)        maintain(q);}int getprev(int x , int &q){    int ans = -1 << 30;    int ins = q;    while(ins)    {        if (v(ins) >= x)            ins = l(ins);        else        {            ans = v(ins);            ins = r(ins);        }    }    return ans;}int getnext(int x , int &q){    int ans = 1 << 30;    int ins = q;    while(ins)    {        if (v(ins) <= x)            ins = r(ins);        else        {            ans = v(ins);            ins = l(ins);        }    }    return ans;}int getrank(int x , int &q){    int ans = 0;    int ins = q;    while(ins)    {        if (x <= v(ins))            ins = l(ins);        else        {            ans += s(l(ins)) + cnt(ins);            ins = r(ins);        }    }    return ans;}int num[N];struct Segment_Node{    int lson , rson , dl , dr , root;};struct Segment_Tree{    Segment_Node Q[150000];    int ind;    Segment_Tree()    {        memset(Q , 0 , sizeof(Q));        ind = 0;    }    int build(int tl , int tr)    {        int q = ++ind;        dl(q) = tl;        dr(q) = tr;        for(register int i = tl ; i <= tr ; ++i)            insert(num[i] , root(q));        if (tl == tr)            return q;        int mid = (tl + tr) >> 1;        lson(q) = build(tl , mid);        rson(q) = build(mid + 1 , tr);        return q;    }    int Seg_getrank(int tl , int tr , int x , int q = 1)    {        if (tl <= dl(q) && dr(q) <= tr)            return getrank(x , root(q));        int mid = (dl(q) + dr(q)) >> 1;        if (tl > mid)            return Seg_getrank(tl , tr , x , rson(q));        else if (tr <= mid)            return Seg_getrank(tl , tr , x , lson(q));        else            return Seg_getrank(tl , mid , x , lson(q)) + Seg_getrank(mid + 1 , tr , x , rson(q));    }    int Seg_getkth(int tl , int tr , int k)    {        int L , R , mid;        L = 0 , R = INF , mid = (L + R + 1 )>> 1;        while(L < R)        {            if (Seg_getrank(tl , tr , mid) < k)                L = mid;            else                R = mid - 1;            mid = (L + R + 1) >> 1;        }        return mid;    }    void modify(int ins , int val , int q = 1)    {        remove(num[ins] , root(q));        insert(val , root(q));        if (dl(q) == dr(q))            return;        int mid = (dl(q) + dr(q)) >> 1;        modify(ins , val , (ins <= mid) ? lson(q) : rson(q));    }    int Seg_getprev(int tl , int tr , int x , int q = 1)    {        if (tl <= dl(q) && dr(q) <= tr)            return getprev(x , root(q));        int mid = (dl(q) + dr(q)) >> 1;        if (tl > mid)            return Seg_getprev(tl , tr , x , rson(q));        else if (tr <= mid)            return Seg_getprev(tl , tr , x , lson(q));        else            return _max(Seg_getprev(tl , mid , x , lson(q)) , Seg_getprev(mid + 1 , tr ,x , rson(q)));    }    int Seg_getnext(int tl , int tr , int x , int q = 1)    {        if (tl <= dl(q) && dr(q) <= tr)            return getnext(x , root(q));        int mid = (dl(q) + dr(q)) >> 1;        if (tl > mid)            return Seg_getnext(tl , tr , x , rson(q));        else if (tr <= mid)            return Seg_getnext(tl , tr , x , lson(q));        else            return _min(Seg_getnext(tl ,mid , x , lson(q)) , Seg_getnext(mid + 1 , tr , x , rson(q)));    }}Ans;int main(){    int n , m;    scanf("%d%d" , &n , &m);    register int i;    for(i = 1 ; i <= n ; ++i)        scanf("%d" , &num[i]);    Ans.build(1 , n);    int sign , a , b , x;    for(i = 1 ; i <= m;  ++i)    {        scanf("%d" , &sign);        switch (sign)        {            case 1:            {                scanf("%d%d%d" , &a , &b , &x);                printf("%d\n" , Ans.Seg_getrank(a , b , x) + 1);                break;            }            case 2:            {                scanf("%d%d%d" , &a , &b , &x);                printf("%d\n" , Ans.Seg_getkth(a , b , x));                break;            }            case 3:            {                scanf("%d%d" , &a , &x);                Ans.modify(a , x);                num[a] = x;                break;            }            case 4:            {                scanf("%d%d%d" , &a , &b , &x);                printf("%d\n" , Ans.Seg_getprev(a , b , x));                break;            }            case 5:            {                scanf("%d%d%d" , &a , &b , &x);                printf("%d\n" , Ans.Seg_getnext(a , b , x));                break;            }        }    }    return 0;}

Sol2:树状数组套主席树。

回忆树状数组:节点i存储的是以位置i为结尾,长度为lowbit(i)的连续一段的信息。树状数组依赖于信息满足区间减法。

求前缀和,只需从位置i开始依次找到上一段并求和即可。

修改某位置:令i不断加上lowbit(i)得到下一段应该被修改的是哪一段,画个图就容易理解了。


回忆主席树:在值域在很小的范围内或者允许离线的情况下,建立可持久化权值线段树。版本i可认为是前i个位置的数所构成的权值线段树。

新版本的权值线段树只需在上一个版本的基础上新建一条路径上的O(logn)个节点即可。

那么求区间第k大只需在差分后的权值线段树上根据size决定向左或向右走即可,避免了二分。

如果支持修改,那么朴素的主席树无法解决问题了。

我们重新定义:类似树状数组,第i个版本表示第i-lowbit(i)+1~i个位置的数所构成的权值线段树。

那么我们询问区间第k大的时候,以两个位置前缀和的size差值作为依据决定走向左子树或右子树。复杂度O(log^2n).

修改时,新建logn颗权值线段树的新版本,复杂度O(log^2n).

Code2:

#include <cstdio>#include <cstring>#include <cctype>#include <iostream>#include <algorithm>using namespace std; inline int getc() {    static const int L = 1 << 20;    static char buf[L], *S = buf, *T = buf;    if (S == T) {        T = (S = buf) + fread(buf, 1, L, stdin);        if (S == T)            return EOF;    }    return *S++;}inline int getint() {    int c;    while(!isdigit(c = getc()) && c != '-');    bool sign = c == '-';    int tmp = sign ? 0 : c - '0';    while(isdigit(c = getc()))        tmp = (tmp << 1) + (tmp << 3) + c - '0';    return sign ? -tmp : tmp;} #define SORT(S, n) sort(S + 1, S + n + 1) #define N 50010#define M 50010int w[N], ope[M][4]; int Global_weigh[N + M], rank_weigh[N + M], top, id;int getins(int x) {    int L = 1, R = id, mid;    while(L < R) {        mid = (L + R) >> 1;        if (rank_weigh[mid] < x)            L = mid + 1;        else            R = mid;    }    return L;} #define l(x) S[x].l#define r(x) S[x].r#define size(x) S[x].sizestruct Node {    int l, r;    short size;}S[8000000];int ind;void Newadd(int Last, int &q, int dl, int dr, int ins, int add) {    if (!q)        q = ++ind;    S[q] = S[Last];    size(q) += add;    if (dl == dr)        return;    int mid = (dl + dr) >> 1;    if (ins <= mid)        Newadd(l(Last), l(q), dl, mid, ins, add);    else        Newadd(r(Last), r(q), mid + 1, dr, ins, add);}int build(int dl, int dr) {    int q = ++ind;    if (dl == dr)        return q;    int mid = (dl + dr) >> 1;    l(q) = build(dl, mid);    r(q) = build(mid + 1, dr);    return q;} int root[N], bit[N];int count(int x, bool self) {    int res = 0;    for(; x; x -= x & -x)        res += self ? size(bit[x]) : size(l(bit[x]));    return res;}inline void setself(int x) {    for(; x; x -= x & -x)        bit[x] = root[x];}inline void setson(int x, bool d) {    for(; x; x -= x & -x)        bit[x] = d ? r(bit[x]) : l(bit[x]);} int getless(int dl, int dr, int ins, int a, int b) {    if (dl == dr)        return dl < ins ? count(b, 1) - count(a, 1) : 0;    int mid = (dl + dr) >> 1;    if (ins <= mid) {        setson(a, 0), setson(b, 0);        return getless(dl, mid, ins, a, b);    }    else {        int res = count(b, 0) - count(a, 0);        setson(a, 1), setson(b, 1);        return res + getless(mid + 1, dr, ins, a, b);    }}int getkth(int dl, int dr, int k, int a, int b) {    if (dl == dr)        return dl;    int mid = (dl + dr) >> 1;    int ls = count(b, 0) - count(a, 0);    if (ls >= k) {        setson(a, 0), setson(b, 0);        return getkth(dl, mid, k, a, b);    }    else {        setson(a, 1), setson(b, 1);        return getkth(mid + 1, dr, k - ls, a, b);    }}int getnum(int dl, int dr, int ins, int a, int b) {    if (dl == dr)        return count(b, 1) - count(a, 1);    int mid = (dl + dr) >> 1;    if (ins <= mid) {        setson(a, 0), setson(b, 0);        return getnum(dl, mid, ins, a, b);    }    else {        setson(a, 1), setson(b, 1);        return getnum(mid + 1, dr, ins, a, b);    }} int main() {    int n = getint(), m = getint();         register int i, j;    for(i = 1; i <= n; ++i)        Global_weigh[++top] = w[i] = getint();         for(i = 1; i <= m; ++i) {        ope[i][0] = getint();        if (ope[i][0] == 3) {            ope[i][1] = getint(), ope[i][3] = getint();            Global_weigh[++top] = ope[i][3];        }        else {            ope[i][1] = getint(), ope[i][2] = getint(), ope[i][3] = getint();            if (ope[i][0] != 2)                Global_weigh[++top] = ope[i][3];        }    }         //Offline-Preatments    SORT(Global_weigh, top);    Global_weigh[0] = -1 << 30;    for(i = 1; i <= top; ++i)        if (Global_weigh[i] != Global_weigh[i - 1])            rank_weigh[++id] = Global_weigh[i];    for(i = 1; i <= n; ++i)        w[i] = getins(w[i]);    for(i = 1; i <= m; ++i)        if (ope[i][0] != 2)            ope[i][3] = getins(ope[i][3]);         //Build the Init President Tree    root[0] = build(1, id);    for(i = 1; i <= n; ++i)        root[i] = ++ind;    for(i = 1; i <= n; ++i)        for(j = i; j <= n; j += j & -j)            Newadd(root[j], root[j], 1, id, w[i], 1);         //Answer the Questions    for(i = 1; i <= m; ++i) {        if (ope[i][0] == 1) {//get-rank            setself(ope[i][1] - 1);            setself(ope[i][2]);            printf("%d\n", getless(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]) + 1);        }        else if (ope[i][0] == 2) {//get-kth            setself(ope[i][1] - 1);            setself(ope[i][2]);            printf("%d\n", rank_weigh[getkth(1, id, ope[i][3], ope[i][1] - 1, ope[i][2])]);        }        else if (ope[i][0] == 3) {//modify            for(j = ope[i][1]; j <= n; j += j & -j)                Newadd(root[j], root[j], 1, id, w[ope[i][1]], -1);            w[ope[i][1]] = ope[i][3];            for(j = ope[i][1]; j <= n; j += j & -j)                Newadd(root[j], root[j], 1, id, w[ope[i][1]], 1);        }        else if (ope[i][0] == 4) {//prev            setself(ope[i][1] - 1);            setself(ope[i][2]);            int less = getless(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]);            setself(ope[i][1] - 1);            setself(ope[i][2]);            printf("%d\n", rank_weigh[getkth(1, id, less, ope[i][1] - 1, ope[i][2])]);        }        else {//succ            setself(ope[i][1] - 1);            setself(ope[i][2]);            int less = getless(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]);            setself(ope[i][1] - 1);            setself(ope[i][2]);            int num = getnum(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]);            setself(ope[i][1] - 1);            setself(ope[i][2]);            printf("%d\n", rank_weigh[getkth(1, id, less + num + 1, ope[i][1] - 1, ope[i][2])]);        }    }         return 0;}




0 0
原创粉丝点击