ps:以下说的"区间"除非强调是权值区间,否则都是下标区间的意思
本文主要记录下,主xi树中一些曾让我困惑的地方
一个疑惑是插入的写法,额还有关于是否要copy旧结点的问题
关于插入数据
1,插入时候,两种更新,一种是pushup形式(就是普通线段树形式),一种是从上而下直接更新(lazy标记永久化写法里好像有这样写过)
//插入写法一,递归到叶子,然后pushup更新sum
void update(int pre,int &now,int l,int r ,int val)
{
now = ++tot;//每个点必定是新开的
lch[now] = lch[pre];
rch[now] = rch[pre];
// cout<<" now lch rch "<<now<<" "<<l<<" "<<r<<endl;
if(l==r )
{
sum[now] = sum[pre] + 1;
return;
}
int mid = (l+r)>>1;
if( val <= mid ) update(lch[pre],lch[now],l,mid,val);
else update(rch[pre],rch[now],mid+1,r,val);
sum[now] = sum[lch[now]] + sum[rch[now]];//pushup
}
//插入的写法二,自上而下地更新sum
void update(int pre,int &now,int l,int r ,int val)
{
now = ++tot;//每个点必定是新开的
lch[now] = lch[pre];
rch[now] = rch[pre];
sum[now] = sum[pre] + 1;
if(l==r )
{
// sum[now] = sum[pre] + 1;
return;
}
int mid = (l+r)>>1;
if( val <= mid ) update(lch[pre],lch[now],l,mid,val);
else update(rch[pre],rch[now],mid+1,r,val);
// sum[now] = sum[lch[now]] + sum[rch[now]];//pushup
}
2,在学树套树的时候,发现树状数组维护的线段树,无需copy。
我的理解是,两个原因,对于一次查询,一个是因为树状数组维护的是log个区间不再是[1,i]而是lowbit~i,还有一个是修改是在线的。
对于区间lowbit~i维护的[1,1e9],我们可以直接修改权值区间,无需copy上一个版本
而求区间第k大那题,为什么需要copy旧版本呢,因为维护的是一个前缀线段树——为了保证[1,i]的信息完整,我们需要将现在改好的一个log长的链,拼到上一个树(即[1,i-1])上以知道[1,i]的信息。
那为什么树套树可以不用呢,因为我们的在线修改就是改的树状数组维护的整个区间lowbit~i,相当于区间第k大那题,我们每次只问1~n区间里第k大的树,那么我们只要维护[1,n]的信息,无需知道任意[1,i]的信息了,两者是等价的。如果要copy的话,也就是我们有知道lowbit~i-1,lowbit~i-2等区间的需求,显然没有...
还有一个疑惑是关于动态开点的定义
问了下庄哥,好像上面所提及的不离散,直接维护[0,1e9]的信息,不算动态开点?就相当于把内存都先开满了N*32,离散化就是可以卡卡常吧
庄哥说他理解的是,用的时候再去申请,一般是带修的问题?更透彻的理解等我再多做点,再补充吧,太菜了现在。
不过不离散的话内存消耗真的很大,带修主席树那题卡空间卡掉了我的define int long long
n,q=1e5,[0,1e9]离散化后[1,2e5]空间常数从N*30*5到N*18*5还是有点大的
写到这里,临时想起一个疑问就是关于root所维护的区间
主席树模板题,root[i]维护的是[1,i]的根节点,可持久化数组模板题,root[i]维护的是每次修改时的根节点,对于原始数组形成的整棵线段树是存在root[0]里的。。。似乎是看有需求查询的历史版本的时间戳??
有大佬知道话,麻烦解答一下qaq