fhq Tree 区间翻转——tag的妙用
Posted zhuier-xquan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了fhq Tree 区间翻转——tag的妙用相关的知识,希望对你有一定的参考价值。
(fhq Tree) 区间翻转——(tag)的妙用
对于(fhq)树的基础知识,参见非旋(fhq) Treap 小记
全篇采用结构体记录树的相关信息:
struct node {
int L,R,val,pri,siz,sur,add;
//左/右儿子、当前节点权值、随机值、当前节点子树大小、区间翻转标记、区间加标记
}t[mxn];
(frak{First.})另一种实现(split)的方法
按子树大小分裂:将这棵树按照某个值(x)分裂成两棵子树,其中一棵包含权值小的前(x)个点,另一棵包括剩下的点。
void split(int nd,int a,int &x,int &y) {
//其中nd表示当前节点,a表示上面所提到的某个值x,而x,y表示分裂成的两棵树的根分别是x,y。
if(!nd) {// 如果这是个假结点(没有这个节点),两棵树根都赋值为0,退出
x=y=0;
return;
}
if(t[t[nd].L].siz<=a) {//如果当前点的左儿子的节点个数小于我们要分裂的节点个数
x=nd;//那么左子树的根为x
split(t[nd].R,a-t[t[nd].L].siz-1,t[nd].R,y);//这时候再去右子树找还不够的那一部分节点(记得要把已经分裂到左子树的节点减去)
} else {//反之,较容易理解
y=nd;
split(t[nd].L,a,x,t[nd].L);
}
update(nd);
}
(frak{Second.})预热——区间加法(tag)
如果要将区间([l,r])上的每个数都加上一个数(d),将整棵树裂为三棵,一棵是([1,l-1])个节点,一棵是([l,r])部分,一棵是([r+1,n])的部分。1可以先将树分裂为([1,l-1])与([l,n]),2接下来将后面一棵子树分裂为([l,r])与([r+1,n]),参考线段树,在([l,r])的根节点的权值和区间加标记上加上(d),最后一定要记得,把树合并回去
inline void update1(int l,int r,int d) {
int x,y,z;
split(rt,l-1,x,y);//1
split(y,r-l+1,y,z);//2
t[y].add+=d;
t[y].val+=d;
rt=merge(merge(x,y),z);
}
在进行合并和分裂时,要对标记进行下放:
//ls(x) 表示x的左儿子
inline void push_down(int x) {
if(t[x].add){
if(ls(x)){
t[ls(x)].add+=t[x].add;
t[ls(x)].val+=t[x].add;
}
if(rs(x)){
t[rs(x)].add+=t[x].add;
t[rs(x)].val+=t[x].add;
}
t[x].add=0;
}
}
(frak{Third.})翻转标记
类比区间加法,得到如下代码:
inline void update2(int l,int r){
int x,y,z;
split(rt,l-1,x,y);
split(y,r-l+1,y,z);//将[l,r]的子树分裂出来
t[y].tur^=1;//打标记
rt=merge(merge(x,y),z);
}
相比起来好像技术含量在更新(
void pushdown(int Rt) {
swap(t[Rt].L,t[Rt].R);//交换左右儿子
t[t[Rt].L].sur^=1;//左右儿子打tag
t[t[Rt].R].sur^=1;
t[Rt].sur=0;//标记清空
}//好吧也没有
(frak{Fourth.}) 一道组合绝佳的例题
(这道题涉及到了区间最大值,可以类比线段树来做)
#include<bits/stdc++.h>
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]
using namespace std;
struct node{
int size,key,val,cx,add,tur,ch[2],maxn;
}t[100010];
int n,m,gs,rt;
int seed=623;
int com,l,r,zhi;
inline void push_up(int x){
t[x].size=t[ls(x)].size+t[rs(x)].size+1;
t[x].maxn=t[x].val;
if(ls(x))
t[x].maxn=max(t[x].maxn,t[ls(x)].maxn);
if(rs(x))
t[x].maxn=max(t[x].maxn,t[rs(x)].maxn);
}
inline void push_down(int x) {
if(t[x].tur){
if(ls(x))
t[ls(x)].tur^=1;
if(rs(x))
t[rs(x)].tur^=1;
ls(x)^=rs(x)^=ls(x)^=rs(x);
t[x].tur=0;
}
if(t[x].add){
if(ls(x)){
t[ls(x)].add+=t[x].add;
t[ls(x)].val+=t[x].add;
t[ls(x)].maxn+=t[x].add;
}
if(rs(x)){
t[rs(x)].add+=t[x].add;
t[rs(x)].val+=t[x].add;
t[rs(x)].maxn+=t[x].add;
}
t[x].add=0;
}
}
int merge(int x,int y){
int now;
push_down(x);push_down(y);
if(!x || !y)return x|y;
if(t[x].key<t[y].key)
now=x,rs(x)=merge(t[x].ch[1],y);
else
now=y,ls(y)=merge(x,t[y].ch[0]);
push_up(now);
return now;
}
void split(int root,int bz,int &x,int &y) {
if(!root){x=y=0;return;}
push_down(root);
if(t[ls(root)].size>=bz)
y=root,split(ls(root),bz,x,ls(y));
else
x=root,split(rs(root),bz-t[ls(root)].size-1,rs(x),y);
push_up(root);
}
inline void insert(int x) {
t[++gs].key=rand();
t[gs].val=x;
t[gs].cx=gs;
t[gs].maxn=x;
t[gs].size=1;
rt=merge(rt,gs);
}
inline void update1(int l,int r,int zhi) {
int x,y,z;
split(rt,l-1,x,y);
split(y,r-l+1,y,z);
t[y].maxn+=zhi;
t[y].add+=zhi;
t[y].val+=zhi;
rt=merge(merge(x,y),z);
}
inline void update2(int l,int r){
int x,y,z;
split(rt,l-1,x,y);
split(y,r-l+1,y,z);
t[y].tur^=1;
rt=merge(merge(x,y),z);
}
inline void query(int l,int r) {
int x,y,z;
split(rt,l-1,x,y);
split(y,r-l+1,y,z);
printf("%d
",t[y].maxn);
rt=merge(merge(x,y),z);
}
int main() {
srand(time(0));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
insert(0);
while(m--) {
scanf("%d",&com);
if(com==1) {
scanf("%d%d%d",&l,&r,&zhi);
update1(l,r,zhi);
}
if(com==2) {
scanf("%d%d",&l,&r);
update2(l,r);
}
if(com==3) {
scanf("%d%d",&l,&r);
query(l,r);
}
}
return 0;
}
(Endcolor{Pink}{??ヽ(°▽°)ノ?})
以上是关于fhq Tree 区间翻转——tag的妙用的主要内容,如果未能解决你的问题,请参考以下文章
洛谷P3391 模板文艺平衡树(Splay)(FHQ Treap)
片段(Java) | 机试题+算法思路+考点+代码解析 2023