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.}) 一道组合绝佳的例题

Luogu P4146 序列终结者

(这道题涉及到了区间最大值,可以类比线段树来做)

#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)

bzoj1251序列终结者——fhq treap

片段(Java) | 机试题+算法思路+考点+代码解析 2023

BZOJ 3223 & 区间翻转

华为OD机试真题Python实现翻转单词顺序真题+解题思路+代码(2022&2023)

「模板」 FHQ_Treap