BZOJ1901 动态排名 线段树套平衡树

(同步个人博客 http://sxysxy.org/blogs/14 到csdn)

动态区间第k小,http://www.lydsy.com/JudgeOnline/problem.php?id=1901

权限题所以大致描述下题面:

初始有一个长度有n得序列a1,a2…an,要求支持查询序列下标区间l, r内第k小的数,支持修改序列内某个数。序列长度n <= 10000(这里我用来练习线段树套平衡树,于是就这样做了。n <= 10000说不定分块就过了…)

做法:

对序列1..n构造线段树,线段树上每一个节点包含一棵平衡树,这棵平衡树包含了当前线段树节点内所有的数。

对平衡树的要求:速度快(显然),支持插入删除(删除是需要修改的时候用的),要支持名次树(再准确点就是支持对于一个x查询<=x的数的个数)。这里我用了AVL树(关于AVL树:http://sxysxy.org/blogs/13)

构造:

构造很简单。构造线段树的同时,把每个线段树节点管理的区间内的所有数插入平衡树。

查询

对于查询”l,r内第k小的数”,使用二分法,二分l, r内第k小的数为m,然后向线段树查询l,r内小于等于m的数的个数(线段数里面套了平衡树,里面平衡树可以查询名次,外面线段树管理区间)。

修改

修改其实是删除+插入。设原来第p个数为u,要将他修改为v,那么只需要线段树内,所有包含位置p的线段树节点内的平衡树删除u,再插入v,即可。

实现上的细节问题见代码

#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <list>
#include <queue>
#include <list>
using namespace std;

//AVL平衡树
typedef struct _AVL
{
    struct _AVL *left, *right;
    int data;
    int height;
    int size;
}AVLNode, *pNode, *AVLTree;
#define HEIGHT(x) ((x)?(x)->height:-1)
#define SIZE(x) ((x)?(x)->size:0)
void maintain(pNode k)
{
    if(k)
    {
        k -> height = max(HEIGHT(k -> left), HEIGHT(k -> right)) + 1;
        k -> size = SIZE(k -> left) + SIZE(k -> right) + 1;
    }
} 
pNode singleRotateLL(pNode k)
{
    pNode k1 = k -> left;
    k -> left = k1 -> right;
    k1 -> right = k;
    maintain(k);
    maintain(k1);
    return k1;
}
pNode singleRotateRR(pNode k)
{
    pNode k1 = k -> right;
    k -> right = k1 -> left;
    k1 -> left = k;
    maintain(k);
    maintain(k1);
    return k1;
}
pNode doubleRotateLR(pNode k)
{
    k -> left = singleRotateRR(k -> left);
    return singleRotateLL(k);
}
pNode doubleRotateRL(pNode k)
{
    k -> right = singleRotateLL(k -> right);
    return singleRotateRR(k);
}
AVLTree insert(AVLTree t, int x)
{
    if(!t)
    {
        t = new AVLNode;
        t -> data = x;
        t -> size = 1;
        t -> left = t -> right = 0;
    }else
    {
        if(x < t -> data)   
        {
            t -> left = insert(t -> left, x);
            if(HEIGHT(t -> left) - HEIGHT(t -> right) == 2)
            {
                if(x < t -> left -> data)
                    t = singleRotateLL(t); 
                else    
                    t = doubleRotateLR(t);    
            }
        }else 
        {
            t -> right = insert(t -> right, x);
            if(HEIGHT(t -> right) - HEIGHT(t -> left) == 2)
            {
                if(x >= t -> right -> data)
                    t = singleRotateRR(t);
                else
                    t = doubleRotateRL(t);
            }
        }
    }
    maintain(t);
    return t;
}
pNode delBalance(pNode k)
{
    if(HEIGHT(k -> right) - HEIGHT(k -> left) == 2)
    {
        if(k -> right)
        {
            if(HEIGHT(k -> right -> right) >= HEIGHT(k -> right -> left))
                k = singleRotateRR(k);
            else
                k = doubleRotateRL(k);
        }
    }
    if(HEIGHT(k -> left) - HEIGHT(k -> right) == 2)
    {
        if(k -> left)
        {
            if(HEIGHT(k -> left -> left) >= HEIGHT(k -> left -> right))
                k = singleRotateLL(k);
            else
                k = doubleRotateLR(k);
        }
    }
    maintain(k);
    return k;
}
AVLTree delNode(AVLTree t, int x)
{
    if(!t)
        return NULL;
    if(x == t -> data)
    {
        if(!t -> right)
        {
            pNode tmp = t;
            t = t -> left;
            free(tmp);
        }else
        {
            pNode tmp = t -> right;
            while(tmp -> left)
                tmp = tmp -> left;
            t -> data = tmp -> data;
            t -> right = delNode(t -> right, t -> data);
        }
        maintain(t);
        return t;
    }
    if(x < t -> data)
    {
        t -> left = delNode(t -> left, x);
    }else
    {
        t -> right = delNode(t -> right, x);
    }
    if(t -> left)
        t -> left = delBalance(t -> left);
    if(t -> right)
        t -> right = delBalance(t -> right);
    if(t)
        t = delBalance(t);
    return t;
}
//<= x 的个数
int Smaller(pNode t, int x)
{
    if(!t)return 0;
    if(x < t -> data)
        return Smaller(t -> left, x);
    else
        return 1 + SIZE(t -> left) + Smaller(t -> right, x);
}


#define MAXN 100002
int last = 0;
struct _SegNode
{
    int ls, rs;   //左右孩子
    AVLTree rt;   //这个节点的平衡树
    int l, r;  
}ns[MAXN<<1];
int data[MAXN];
int build(int l, int r)
{
    int cur = ++last;
    ns[cur].l = l;
    ns[cur].r = r;
    ns[cur].rt = NULL;

    for(int i = l; i <= r; i++) //将区间l,r内的数全部插入平衡树
        ns[cur].rt = insert(ns[cur].rt, data[i]);
    if(l == r)
        return cur;
    int mid = (l+r)>>1;
    if(l <= mid)
    {
        ns[cur].ls = build(l, mid);
        ns[cur].rs = build(mid+1, r);
    }
    return cur;
}
//查询区间l, r内 <= n的数的个数
//第一个参数c是线段树节点的下标
int queryTree(int c, int l, int r, int n)
{
    if(ns[c].l == l && ns[c].r == r)
        return Smaller(ns[c].rt, n);  // <= n的数的个数
    int ls= ns[c].ls;
    int rs = ns[c].rs;
    if(r <= ns[ls].r)   //l,r完全在左边
        return queryTree(ls, l, r, n);
    else if(l >= ns[rs].l) //右边
        return queryTree(rs, l, r, n);
    else  //跨越中点,两段之和
        return queryTree(ls, l, ns[ls].r, n) + queryTree(rs, ns[rs].l, r, n);
}
//修改第pos个数为v
void update(int c, int pos, int v)
{ 
    ns[c].rt = delNode(ns[c].rt, data[pos]);   //删掉原来的数
    ns[c].rt = insert(ns[c].rt, v);            //插入新的数
    int ls = ns[c].ls;
    int rs = ns[c].rs;
    if(ls && pos >= ns[ls].l && pos <= ns[ls].r)  //更新左右子树
        update(ls, pos, v);
    else if(rs && pos >= ns[rs].l && pos <= ns[rs].r)
        update(rs, pos, v);
}
void change(int pos, int v)
{
    update(1, pos, v);
    data[pos] = v;     //记录最后一次data[pos]的值
}
int solve(int l, int r, int k)
{
    int x = 0;
    int y = 0x7fffffff;   //二分答案
    int ans = 0;
    while(x <= y)
    {
        int mid = (x+y)>>1;
        int f = queryTree(1, l, r, mid); // l,r内 <= mid的数的个数
        if(f >= k)    
        {
            y = mid-1;
            ans = mid;
        }
        else
            x = mid+1;
    }
    return ans;
}
int main()
{
    int n, m;
    scanf("%d %d", &n, &m);;
    for(int i = 1; i <= n; i++)
        scanf("%d", data+i); 
    build(1, n);
    while(m--)
    {
        char c;
        while(c = getchar())
            if(c == 'Q' || c == 'C')break;
        if(c == 'Q')
        {
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            printf("%d\n", solve(a, b, c));
        }else if(c == 'C')
        {
            int a, b;
            scanf("%d %d", &a, &b);
            change(a, b);
        }
    }
    return 0;
}
发布了29 篇原创文章 · 获赞 18 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览