FHQ Treap 之区间操作

例题链接

您需要写一种数据结构(可参考题目标题),来维护一个有序数列。

其中需要提供以下操作:翻转一个区间,例如原有有序序列是 \(5\ 4\ 3\ 2\ 1\),翻转区间是 \([2,4]\) 的话,结果是 \(5\ 2\ 3\ 4\ 1\)

前置知识:FHQ Treap

参见FHQ Treap (无旋 Treap) 详解

平衡树维护区间信息

原理

普通平衡树维护的是一个按照权值有序的数据结构。

但是,我们也可以用此来维护区间信息——让其中的元素按照其在区间里的位置有序即可。更加通俗易懂一点,就是按照其数组下标有序

区别与联系

FHQ Treap 中简而言之就是:原本填写权值的地方,全部改成区间下标。

分裂

比如说原本的 \(split(p,x,l,r)\),表示将树 \(p\)(以 \(p\) 为根节点的树)以 \(x\) 为键值分裂为两棵树 \(l,r\),使得树 \(l\) 中所有节点的权值小于等于 \(x\),树 \(r\) 中所有节点的权值大于 \(x\)


那么现在维护区间信息时的 \(split(p,x,l,r)\) 则是表示:将树 \(p\) 维护的区间的前 \(x\) 项分裂至树 \(l\),剩余的分裂至树 \(r\)

参考代码

void split(int p,int x,int &l,int &r){
    if(p==0)l=r=0;
    else{
        down(p);
        //注意这里的区别
        if(t[t[p].left].size+1<=x){
            l=p;
            split(t[l].right,x-t[t[p].left].size-1,t[l].right,r);
        }else{
            r=p;
            split(t[r].left,x,l,t[r].left);
        }update(p);
    }
}

值得一提的是,\(merge(l,r)\) 不需要改动。

插入节点

这不是例题中的操作。

在区间末尾增加一个元素 \(x\)

void insert(int x){
    root=merge(root,create(x));
}

区间翻转

类似于线段树,打个懒标记来判断左右子树是否应当交换左右子节点即可。

例题 AC 代码

//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#include<random>
using namespace std;
const int N=100000;
mt19937 Rand(time(0));
int root;
//封装FHQ Treap
struct treap{
	struct node{
		int value,rand,size;
		int left,right;
		bool flag;
	}t[N+1];
	
	void update(int p){
		t[p].size=t[t[p].left].size+t[t[p].right].size+1;
	}
	void down(int p){
		if(t[p].flag){
			t[t[p].left].flag^=true;
			t[t[p].right].flag^=true;
			swap(t[p].left,t[p].right);
			t[p].flag=false;
		}
	}//注意是按照排名分裂
	void split(int p,int x,int &l,int &r){
		if(p==0)l=r=0;
		else{
			down(p);
			if(t[t[p].left].size+1<=x){
				l=p;
				split(t[l].right,x-t[t[p].left].size-1,t[l].right,r);
			}else{
				r=p;
				split(t[r].left,x,l,t[r].left);
			}update(p);
		}
	}
	int merge(int l,int r){
		if(l==0)return r;
		if(r==0)return l;
		if(t[l].rand<t[r].rand){
			down(l);
			t[l].right=merge(t[l].right,r);
			update(l);
			return l;
		}else{
			down(r);
			t[r].left=merge(l,t[r].left);
			update(r);
			return r;
		}
	}
	
	int create(int x){
		static int top;
		t[++top]={x,(int)Rand(),1,0,0};	
		return top;
	}
	void insert(int x){
		int l,r;
		split(root,x,l,r);
		root=merge(merge(l,create(x)),r);
	}
	void reserve(int l,int r){
		int pl,pr;
		split(root,r,pl,pr);
		split(pl,l-1,pl,root);
		t[root].flag^=true;
		root=merge(merge(pl,root),pr);
	}
	void print(int p=root){
		if(p==0)return;
		down(p);
		print(t[p].left);
		printf("%d ",t[p].value);
		print(t[p].right);
	}
}t;
int a[N+1];
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)t.insert(i);
	for(int i=1;i<=m;i++){
		int l,r;
		scanf("%d %d",&l,&r);
		t.reserve(l,r);
	}t.print();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

练习题

让你们感受一下人性的险恶!!

两道 省选/NOI−,反正我是调了 \(\text{7h}\),看代码就能懂了……

posted @ 2025-07-20 15:04  TH911  阅读(24)  评论(0)    收藏  举报