动态区间第k小(主席树+线段树套树状数组)

静态区间第k小问题,是给你一个序列,每次询问序列中的一个区间中的第k小数,这个问题用普通的主席树就可以解决。动态区间第k小问题就是在静态的基础上加上了修改操作,也就是每次除了询问区间第k小之外,还可以修改序列中的某个数。因为这里涉及到了修改操作,我们用只用主席树好像难以完成这个问题,下面我们简单来分析一下这个问题。
我们知道在主席树中,第i棵线段树T[i]维护的是序列[1,i]中的数,我们设想一下,如果我们修改一个数,那么这个修改将影响到的线段树有T[i], T[i + 1] … ..T[n],如果我们用普通暴力的方法,每次都更新所有的这些线段树,那么时间复杂度爆表,这种做法肯定是不可行的。那么我们要想另外一种比O(n)更新还快的操作来解决这个问题。我们分析主席树的性质可以得到,如果修改了一个树,对那些线段树的影响都是一样的,比如,序列中原来位置i上的数是x,现在我们把它修改成y,那么对于需要修改的线段树,T[i], T[i + 1]…….T[n],每棵线段树的影响都是对应线段树上减去了数x,加上了数y,到了这里我们可以联想到树状数组,好像可以优化这个操作。我们让树状数组上的每个点对应一棵线段树,第i棵线段树维护区间[i - lowbit(i) + 1, i]区间的变化量就行。

下面是zoj2212的ac代码(第一次写数据结构套数据结构,写的比较丑):

#include<bits/stdc++.h>
using namespace std;
const int maxn = 6e4 + 10;
int n, q;
struct node{
    int L, R;
    int sum;
    node(){
        sum = 0;
    }
}Tree[2500010];//线段树的节点
int cnt;
int s[maxn];//树状数组,每个点表示一颗线段树的根节点
int X[maxn], Y[maxn];//辅助数组
int T[maxn];//第i棵线段树的根节点
int a[maxn];//原数组
int H[maxn];//原数组排序之后的数组
int m;//总共不同数的个数,也就是每棵线段树的大小
struct ask{
    int l, r;
    int k;
}Ask[maxn];//由于要整体先hash,所以必须离线处理
void init()
{
    cnt = 0;
    sort(H, H + m);
    m = unique(H, H + m) - H;
}
int Hash(int x)
{
    return lower_bound(H, H + m, x) - H;
}
int lowbit(int x)
{
    return x&(-x);
}
int build(int l, int r)//建立一棵空树T[0]
{
    int f = cnt++;
    Tree[f].sum = 0;
    if(l == r) return f;
    int mid = (l + r)>>1;
    Tree[f].L = build(l, mid);
    Tree[f].R = build(mid + 1, r);
    return f;
}
int insert(int pa, int x, int value, int l, int r)
{
    int now = cnt++;
    Tree[now].sum = Tree[pa].sum + value;
    if(l == r) return now;
    int mid = (l + r)>>1;
    if(x <= mid)
    {
        Tree[now].R = Tree[pa].R;
        Tree[now].L = insert(Tree[pa].L, x, value, l, mid);
    }
    else
    {
        Tree[now].L = Tree[pa].L;
        Tree[now].R = insert(Tree[pa].R, x, value, mid + 1, r);
    }
    return now;

}
void update(int loc, int x, int value)//树状数组更新
{
    for(int i = loc; i <= n; i += lowbit(i))
    {
        s[i] = insert(s[i], x, value, 0, m - 1);
    }
}
int get(int loc, int *term)//树状数组求和
{
    int ans = 0;
    for(int i = loc; i >= 1; i -= lowbit(i))
    {
        ans += Tree[Tree[term[i]].L].sum;
    }
    return ans;
}
int query(int L, int R, int k, int l, int r, int *term1, int *term2, int p1, int p2)
{
    if(l == r) return l;
    int mid = (l + r)>>1;
    int d1 = Tree[Tree[p2].L].sum - Tree[Tree[p1].L].sum;
    int x1 = get(R, term1);
    int x2 = get(L - 1, term2);
    int d2 = x1 - x2;
    int d = d1 + d2;
    if(k <= d)
    {
        for(int i = R; i >= 1; i -= lowbit(i))
        {
            X[i] = Tree[X[i]].L;

        }
        for(int i = L - 1; i >= 1; i -= lowbit(i))
        {
            Y[i] = Tree[Y[i]].L;
        }
        return query(L, R, k, l, mid, X, Y, Tree[p1].L, Tree[p2].L);
    }
    else
    {
        for(int i = R; i >= 1; i -= lowbit(i))
        {
            X[i] = Tree[X[i]].R;

        }
        for(int i = L - 1; i >= 1; i -= lowbit(i))
        {
            Y[i] = Tree[Y[i]].R;
        }
        return query(L, R, k - d, mid + 1, r, X, Y, Tree[p1].R, Tree[p2].R);
    }

}
int main()
{
    //freopen("C:\\Users\\creator\\Desktop\\in.txt","r",stdin) ;
    //freopen("C:\\Users\\creator\\Desktop\\out.txt","w",stdout) ;
    int Case;
    scanf("%d", &Case);
    while(Case--)
    {
        scanf("%d%d", &n, &q);
        m = 0;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            H[m++] = a[i];
        }
        char type[10];
        for(int i = 1; i <= q; i++)
        {
            scanf("%s", type);
            if(type[0] == 'Q')
            {
                scanf("%d%d%d", &Ask[i].l, &Ask[i].r, &Ask[i].k);
            }
            else
            {
                scanf("%d%d", &Ask[i].l, &Ask[i].r);
                Ask[i].k = -1;
                H[m++] = Ask[i].r;//后来更新的值也要hash进去
            }
        }
        init();
        T[0] = build(0, m - 1);
        for(int i = 1; i <= n; i++)
        {
            T[i] = insert(T[i - 1], Hash(a[i]), 1, 0, m - 1);
        }
        for(int i = 1; i <= n; i++)
        {
            s[i] = T[0];
        }
        for(int i = 1; i <= q; i++)
        {
            if(Ask[i].k > 0)
            {
                for(int j = Ask[i].r; j >= 1; j -= lowbit(j))
                {
                    X[j] = s[j];
                }
                for(int j = Ask[i].l - 1; j >= 1; j -= lowbit(j))
                {
                    Y[j] = s[j];
                }
                printf("%d\n", H[query(Ask[i].l, Ask[i].r, Ask[i].k, 0, m - 1, X, Y, T[Ask[i].l - 1], T[Ask[i].r])]);
            }
            else
            {
                update(Ask[i].l, Hash(a[Ask[i].l]), -1);
                update(Ask[i].l, Hash(Ask[i].r), 1);
                a[Ask[i].l] = Ask[i].r;
            }
        }

    }
    return 0;
}


发布了150 篇原创文章 · 获赞 212 · 访问量 15万+
展开阅读全文

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

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

分享到微信朋友圈

×

扫一扫,手机浏览