0%

思路讲解

【算法讲解150【扩展】有序表专题3-替罪羊树】 https://www.bilibili.com/video/BV1GqDSYbEPR/?share_source=copy_web&vd_source=6ca0bc05e7d6f39b07c1afd464edae37

总体上来说替罪羊树的思想还是比较简单的,但是其实真写起来还是会有挺多要注意的地方的。

prex,sufx如何改用kth和getrank实现?说白了,其实我们能保证 kth(getrank(val))valkth(getrank(val))≥val,那我们自然也可以保证 kth(getrank(val)1)<valkth(getrank(val)-1)<val

1
2
3
4
5
6
7
inline KEY prex(KEY val){ 	// 前驱定义为小于 x,且最大的数
return kth(getrank(val)-1);
}
inline KEY sufx(KEY val){ // 后继定义为大于 x,且最小的数
return kth(getrank(val+1)); // 所有<=25的数就是<26的数的个数
// kth(getrank(val)+1); -> 89851 而非 84185,是因为kth(getrank(val))本身就可能>val
}

AC代码

https://www.luogu.com.cn/record/218793914

心路历程(WA,TLE,MLE……)

解决RE,MLE是靠把删除造成重构的代码给删了(觉得没必要)

WA

https://www.luogu.com.cn/record/218789914

初步查明,是前驱后继未判断节点是否完全删除。

1
2
3
4
5
6
7
8
9
void prex(int node,KEY val){
if(key[node]>=val){
if(ls[node]!=0) prex(ls[node],val);
}else{
if(!isdel[node]) ans=max(ans,key[node]);
if(rs[node]!=0) prex(rs[node],val);
return;
}
}

当然,上面这个代码也有点问题,删除后的节点,由于没更新的缘故,需要考虑更多情况

1
2
3
4
5
6
7
8
9
10
11
12
13
void prex(int node,KEY val){
if(key[node]>=val){
if(ls[node]!=0) prex(ls[node],val);
}else{
if(!isdel[node]){ // 如果该节点没有删除,那看一看更大的,因为更小的不可能比这个优越
ans=max(ans,key[node]);
if(rs[node]!=0) prex(rs[node],val);
}else{
if(ls[node]!=0) prex(ls[node],val);
if(rs[node]!=0) prex(rs[node],val);
}
}
}

WA2

https://www.luogu.com.cn/record/218793645

初步查明,原因是删除节点后,又加入了该节点,需要修改节点状态重新为isdel=false

思路讲解

那我们还是要确认一下,我们的这个模版啊,问题不是很大,所以我们来用cf的题目测一下。

就改了一下这个query部分,其他都没动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// u v 为node,l,r为区间,k为区间第k小(实际上来说,表示区间的是u,v。l,r为值域)
int query(int u,int v,int l,int r,int k){
if(l==r){
if(sum[v]-sum[u]>=k) return l;
else return INF;
}
// 我们这样就知道了
int lsum=sum[ls[v]]-sum[ls[u]];
int mid=l+r>>1;
int ans1=INF,ans2=INF;
if(lsum>=k){
ans1=query(ls[u],ls[v],l,mid,k);
}
if(ans1==INF && sum[rs[v]]-sum[rs[u]]>=k){
ans2=query(rs[u],rs[v],mid+1,r,k);
}
// ans1= ans1==0?INF:ans1;ans2= ans2==0?INF:ans2;
return min(ans1,ans2);
}

AC代码

https://codeforces.com/contest/840/submission/321605622

心路历程(WA,TLE,MLE……)

思路讲解

给一个数列,每次询问一个区间内有没有一个数出现次数超过一半。

AC代码

https://www.luogu.com.cn/record/218724261

心路历程(WA,TLE,MLE……)

我超,这个POI的数据还是挺严格的,我的这个主席树codeforces和洛谷模版都过了,就这道题目没有过。

1
2
3
1 1
1
1 1

黑体部分,要放在比较下面,不要放在版本更新的上面,否则可能未版本更新就出去了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 处理静态区间第 k 小问题中,因为权值线段树,x=1
void add(int node,int ver,int p,ll x){
clone(node);
sum[idx]+=x;
// 如果当前遍历到的点是一个版本的根,那么现在的点idx就是目前这个版本的根
if(root[ver]==node){
++Cver;
root[Cver]=idx;
}
if(p==L[node] && p==R[node]){
return;
}
if(p>=L[ls[node]] && p<=R[ls[node]]){
int nals=ls[idx];
ls[idx]=idx+1;
add(nals,ver,p,x);
}else{
int nars=rs[idx];
rs[idx]=idx+1;
add(nars,ver,p,x);
}
}

思路讲解

主要参考这段代码

https://github.com/algorithmzuo/algorithm-journey/blob/main/src/class157/Code02_PointPersistent2.java

参考题解

【算法讲解157【挺难】可持久化线段树和标记永久化】 【精准空降到 45:11】 https://www.bilibili.com/video/BV1bSc6eREi4/?share_source=copy_web&vd_source=6ca0bc05e7d6f39b07c1afd464edae37&t=2711

AC代码

https://www.luogu.com.cn/record/218615977

心路历程(WA,TLE,MLE……)

https://www.luogu.com.cn/record/218612872

这个 k-lsum 要小心,记得要加,因为往r走相当于抛掉了一些东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// u v 为node,l,r为区间,k为区间第k小
int query(int u,int v,int l,int r,int k){
if(l==r){
return l;
}
// 我们这样就知道了
int lsum=sum[ls[v]]-sum[ls[u]];
int mid=l+r>>1;
if(lsum>=k){
return query(ls[u],ls[v],l,mid,k);
}else{ // 这个 k-lsum 要小心,记得要加,因为往r走相当于抛掉了一些东西
return query(rs[u],rs[v],mid+1,r,k-lsum);
}
}