树套树学习

标题扯的有点宽泛,树套树分很多种吧,会慢慢学习下,然后学习笔记会更新到这里(立flag 中。。。)

线段树套树状数组

从这个题 “Dynamic” Inversion 说起吧!就是求动态逆序对,比较经典的问题,解法也比较多。

继续,题意就是给定一个序列,会不断的删除数字,问每次删除完数字后的逆序对数是?

首先,直接暴力求解初始的逆序对Tot,这个利用树状数组就可以轻松实现!

那么每次删完一个数后,我们只需考虑这个数贡献的逆序对是多少就行了,最后从Tot中减掉就是新的答案,那么如何去统计这个数 x 的贡献呢?

就是统计序列中x 前面比它大的数的个数 + 后面比它小的数的个数,也就是我们呢需要维护 前面比它大 这两个信息,采用的方法就是建一颗线段树,线段树的每一层的每个节点表示的 [L,R] 区间是内数是有序的,也就是下面的代码:

void build(int L,int R,int d)
{
    for(int i = L;i <= R;i ++) {
        T[d][i] = T[d-1][i];//每一层的序列
        del[d][i] = 0;
    }
    if(L == R) return ;
    int M = (L + R) >> 1;
    build(L,M,d+1);
    build(M+1,R,d+1);
    sort(T[d]+L,T[d]+R+1);
}

由于每个节点内数有序,这样一来如果查询第 i 个数前面有多少个比它大的,直接像线段树区间查询那样,查询区间[1,i1] ,这样就能保证查询的数在它前面,然后每个节点上二分就能得到比它大的数的个数(因为有序了嘛)!
代码:

void query(int d,int L,int R,int l,int r,int val,int tp)
{
    if(l <= L && r >= R) {
        int p = getpos(L,R,val,d);//[L,p] < val
        //要减掉被删除的数的个数
        if(tp) {
            Ans -= (p - L + 1) - read(del[d],L-1,p);
        }
        else {
            Ans -= (R - p) - (read(del[d],L-1,R) - read(del[d],L-1,p));
        }
        return ;
    }
    if(L >= R) return;
    int M = (L + R) >> 1;
    if(l <= M) query(d + 1,L,M,l,r,val,tp);
    if(r > M ) query(d + 1,M + 1,R,l,r,val,tp);
}

等等,不是还会删除一些数字吗?嗯,树状数组就是用来维护这个信息的,线段树的每个节点下维护一颗树状数组,用来求区间内被删除的数的个数!

在每次删除数后,我们需要去在树状数组中更新这个信息,只需找到这个数在节点中的位置,然后在树状数组中单点更新就好了!
代码:

void updatedel(int d,int L,int R,int i,int val)
{
    if(L == R) {
        update(del[d],L,R);
        return ;
    }
    if(L >= R) return ;
    int M = (L + R) >> 1;
    if(i <= M) updatedel(d+1,L,M,i,val);
    else updatedel(d+1,M+1,R,i,val);
    int p = getpos(L,R,val,d);//找到对应位置
    update(del[d],p,R);//更新树状数组
}

仔细撸一遍代码就懂啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值