ZOJ 2112 & BZOJ 1901 Dynamic Rankings(主席树 单点更新 区间第K大)

题意:有n个数,m次操作,操作有两种:

Q l r k :查询区间[l, r]中第K大的数

C i v   :将第i个数改为v


思路 :在静态主席树上增加了一个单点更新操作。


见博客(点击打开链接

每次更新一个数,需要更新的是T[i], T[i+1]... ...T[n](该数所在的树以及它后面的所有树)

因为每棵树T[i]所记录的都是前缀(1到i的数出现的次数) 因此,改变i,会影响i到n的所有树

这样,每次更新的复杂度最坏为O( n),最坏更新q次即为O( n×m) 复杂度相当庞大,很明显这样做是不行的

 

那怎么办呢?

我们可以发现,对于改变i处的数这个操作,对于T[i], T[i+1]... ...T[n]这些树的影响是相同的

  都只改变了  “原来i处的数 的数量”  和  “现在i处的数 的数量” 这两个值而已

我们只要在原来的基础上增加一类树, 用它们来维护更新掉的数

即用树状数组来记录更新,每次更新 logn棵树

 

下面来演示一下建树到查询的过程:

比如此题的第一个案例

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

先将序列以及要更新的数(C操作)离散化  

即3 2 1 4 7 、 6  ---->(排序) ----> 1 2 3 4 6 7  

那么我们就需要建一棵这样的树:

(圈里的都是结点的编号, 4、5、6、9、10、11号结点代表的分别是1、2、3、4、6、7)

(4、5、9、10你也可以任意作为6或11的儿子, 递归生成的是类似这样的, 这并不重要)

 

对于3 2 1 4 7(先不管需要更新的6)建完树见下图(建树过程同静态的,不明白的戳这里,上篇博客有讲)

(红色的是个数, 相同结点的个数省略了,同前一棵树)

 

对于C操作之前的Q,就跟静态的类似,减一减 找就好了

 

然后下面要更新了

对于更新, 我们不改变这些已经建好的树, 而是另建一批树S,用来记录更新,而这批线段树,我们用树状数组来维护

也就是树状数组的每个节点都是一颗线段树

一开始,S[0]、S[1]、S[2]、S[3]、S[4]、S[5](树状数组的每个节点)这些都与T[0]相同(也就是每个节点建了一棵空树)

对于C 2 6 这个操作, 我们只需要减去一个2,加上一个6即可

对于减去2

(树状数组i+lowbit(i)为i的父亲节点, 修改i,就要把i的所有父亲节点都修改了)

2在树状数组中出现的位置是 2、2+lowbit(2)=4 这两个位置,    

因此要更新的是S[2]和S[4]这两个节点中的树

删去2后是这样

加上一个6 (同样是对于2号位置, 因此需要更新的仍是S[2]和S[4])

加上之后是这样

 

 

 当查询的时候, 对树T的操作与静态的一致,另外再加上S树的值就好了


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 6e4+5; //n+m
int a[maxn], Hash[maxn];
int T[maxn], lson[maxn<<5], rson[maxn<<5], sum[maxn<<5];
int S[maxn];
int n, m, tot;
struct node
{
    int l, r, k;
    bool Q;
}op[maxn];

int build(int l, int r)
{
    int rt = tot++;
    sum[rt] = 0;
    if(l != r)
    {
        int mid = (l+r)/2;
        lson[rt] = build(l, mid);
        rson[rt] = build(mid+1, r);
    }
    return rt;
}

int update(int pre, int l, int r, int x, int val)
{
    int rt = ++tot;
    lson[rt] = lson[pre], rson[rt] = rson[pre], sum[rt] = sum[pre]+val;
    if(l < r)
    {
        int mid = (l+r)/2;
        if(x <= mid)
            lson[rt] = update(lson[pre], l, mid, x, val);
        else
            rson[rt] = update(rson[pre], mid+1, r, x, val);
    }
    return rt;
}

int lowbit(int x)
{
    return x&(-x);
}

int use[maxn];

int Sum(int x)
{
    int res = 0;
    while(x)
    {
        res += sum[lson[use[x]]];
        x -= lowbit(x);
    }
    return res;
}

int query(int u, int v, int lr, int rr, int l, int r, int k)
{
    if(l >= r) return l;
    int mid = (l+r)/2;
    int tmp = Sum(v)-Sum(u)+sum[lson[rr]]-sum[lson[lr]];
    if(tmp >= k)
    {
        for(int i = u; i; i -= lowbit(i))
            use[i] = lson[use[i]];
        for(int i = v; i; i -= lowbit(i))
            use[i] = lson[use[i]];
        return query(u, v, lson[lr], lson[rr], l, mid, k);
    }
    else
    {
        for(int i = u; i; i -= lowbit(i))
            use[i] = rson[use[i]];
        for(int i = v; i; i -= lowbit(i))
            use[i] = rson[use[i]];
        return query(u, v, rson[lr], rson[rr], mid+1, r, k-tmp);
    }
}

void modify(int x, int p, int d)
{
    while(x <= n)
    {
        S[x] = update(S[x], 1, m, p, d);
        x += lowbit(x);
    }
}

int main(void)
{
    int t;
    cin >> t;
    while(t--)
    {
        int q;
        scanf("%d%d", &n, &q);
        tot = m = 0;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            Hash[++m] = a[i];
        }
        for(int i = 0; i < q; i++)
        {
            char cmd[10];
            scanf(" %s", cmd);
            if(cmd[0] == 'Q')
            {
                scanf("%d%d%d", &op[i].l, &op[i].r, &op[i].k);
                op[i].Q = 1;
            }
            else
            {
                scanf("%d%d", &op[i].l, &op[i].r);
                op[i].Q = 0;
                Hash[++m] = op[i].r;
            }
        }
        sort(Hash+1, Hash+1+m);
        int mm = unique(Hash+1, Hash+1+m)-Hash-1;
        m = mm;
        T[0] = build(1, m);
        for(int i = 1; i <= n; i++)
            T[i] = update(T[i-1], 1, m, lower_bound(Hash+1, Hash+1+m, a[i])-Hash, 1);
        for(int i = 1; i <= n; i++)
            S[i] = T[0];
        for(int i = 0; i < q; i++)
        {
            if(op[i].Q)
            {
                for(int j = op[i].l-1; j; j-=lowbit(j))
                    use[j] = S[j];
                for(int j = op[i].r; j; j-=lowbit(j))
                    use[j] = S[j];
                printf("%d\n", Hash[query(op[i].l-1, op[i].r, T[op[i].l-1], T[op[i].r], 1, m, op[i].k)]);
            }
            else
            {
                modify(op[i].l, lower_bound(Hash+1, Hash+1+m, a[op[i].l])-Hash, -1);
                modify(op[i].l, lower_bound(Hash+1, Hash+1+m, op[i].r)-Hash, 1);
                a[op[i].l] = op[i].r;
            }
        }
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值