标题扯的有点宽泛,树套树分很多种吧,会慢慢学习下,然后学习笔记会更新到这里(立flag 中。。。)
线段树套树状数组
从这个题 “Dynamic” Inversion 说起吧!就是求动态逆序对,比较经典的问题,解法也比较多。
继续,题意就是给定一个序列,会不断的删除数字,问每次删除完数字后的逆序对数是?
首先,直接暴力求解初始的逆序对Tot,这个利用树状数组就可以轻松实现!
那么每次删完一个数后,我们只需考虑这个数贡献的逆序对是多少就行了,最后从Tot中减掉就是新的答案,那么如何去统计这个数 x 的贡献呢?
就是统计序列中
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
个数前面有多少个比它大的,直接像线段树区间查询那样,查询区间
代码:
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);//更新树状数组
}
仔细撸一遍代码就懂啦!