码迷,mamicode.com
首页 > 其他好文 > 详细

fhq Tree 区间翻转——tag的妙用

时间:2020-05-02 18:41:46      阅读:58      评论:0      收藏:0      [点我收藏+]

标签:can   line   i++   lan   query   ace   lse   down   int   

\(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\n",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;
}

\(End\color{Pink}{??ヽ(°▽°)ノ?}\)

fhq Tree 区间翻转——tag的妙用

标签:can   line   i++   lan   query   ace   lse   down   int   

原文地址:https://www.cnblogs.com/zhuier-xquan/p/12818802.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!