P6136 【模板】普通平衡樹(資料加強版)

liuboom發表於2024-12-06

P6136 【模板】普通平衡樹(資料加強版)

題目描述

您需要寫一種資料結構(可參考題目標題),來維護一些整數,其中需要提供以下操作:

  1. 插入一個整數 \(x\)
  2. 刪除一個整數 \(x\)(若有多個相同的數,只刪除一個)。
  3. 查詢整數 \(x\) 的排名(排名定義為比當前數小的數的個數 \(+1\))。
  4. 查詢排名為 \(x\) 的數(如果不存在,則認為是排名小於 \(x\) 的最大數。保證 \(x\) 不會超過當前資料結構中數的總數)。
  5. \(x\) 的前驅(前驅定義為小於 \(x\),且最大的數)。
  6. \(x\) 的後繼(後繼定義為大於 \(x\),且最小的數)。

本題強制線上,保證所有操作合法(操作 \(2\) 保證存在至少一個 \(x\),操作 \(4,5,6\) 保證存在答案)。

輸入格式

第一行兩個正整數 \(n,m\),表示初始數的個數和操作的個數。

第二行 \(n\) 個整數 \(a_1,a_2,a_3,\ldots,a_n\),表示初始的數

接下來 \(m\) 行,每行有兩個整數 \(\text{opt}\)\(x'\)\(\text{opt}\) 表示操作的序號($ 1 \leq \text{opt} \leq 6 \(),\)x'$ 表示加密後的運算元。

我們記 \(\text{last}\) 表示上一次 \(3,4,5,6\) 操作的答案,則每次操作的 \(x'\) 都要異或\(\text{last}\) 才是真實的 \(x\)。初始 \(\text{last}\)\(0\)

輸出格式

輸出一行一個整數,表示所有 \(3,4,5,6\) 操作的答案的異或和

限制與約定

對於 \(100\%\) 的資料,\(1\leq n\leq 10^5\)\(1\leq m\leq 10^6\)\(0\leq a_i,x\lt 2^{30}\)

因為
我調了一下午spaly沒調出來
我實在是太想學FHQ-Treap了
所以這是一篇FHQ-Treap的總結:

FHQ-Treap只需要支援兩種操作:分裂和合並
所以十分的好寫(或許)

首先介紹一下FHQ-Treap需要的陣列:
siz ; val ; pri: 分別表示以 x 為根的子樹的大小,節點 x 的值,節點 x 的優先順序(由 rand() 產生,其目的在維持樹的平衡)
ch[N][2]: 節點x的左右兒子的位置(感覺有點像 0/1Tried 的寫法)

分裂:

split(int x,int k) 表示將以\(x\)為根節點的樹分裂成兩顆樹a,b
(x中的節點按照與k的大小關係被分入a,b中)

Code:

pi split(int x,int k)
{
	if(!x)return pi(0,0);
	if(val[x]<k)//完全在右子樹 
	{
		pi y=split(ch[x][1],k);
		ch[x][1]=y.a;//將分裂後的左子樹與x合併(k不在y.a中) 
		upd(x);
		return pi(x,y.b);//最後分裂出了 x  y.b 返回這兩棵樹 
	}
	else//存在於左子樹內 
	{
		pi y= split(ch[x][0],k);
		ch[x][0]=y.b;
		upd(x);
		return pi(y.a,x);
	} 
}

合併:

int merge(int x,int y) 表示將兩棵樹x,y合併將新的根節點返回

Code:

int merge(int x,int y)
{
	if(!x||!y)return x+y;
	if(pri[x]<pri[y])//有點左偏樹的味道 
	{
		ch[x][1]=merge(ch[x][1],y);
		upd(x);
		return x;
	}
	else
	{
		ch[y][0]=merge(x,ch[y][0]);//始終滿足y子樹內任意值比x大
		upd(y);
		return y; 
	}
}

插入:

先新建一個節點表示k,其下標為++cnt
insert(int k):
將rt按照k拆成兩棵樹x,y
然後合併merge(merge(x,cnt),y)

void insert(int k)
{
	val[++cnt]=k;pri[cnt]=rand()*5+rand();siz[cnt]=1;
	pi x=split(rt,k);
	rt=merge(merge(x.a,cnt),x.b);
}

刪除:

void del(int k)
{
	pi x,y;
	x=split(rt,k);//x.a:[0,k-1] x.b:[k,inf] 
	y=split(x.b,k+1);//y.a[k,k] y.b:[k+1,inf]
	y.a=merge(ch[y.a][0],ch[y.a][1]);
	x.b=merge(y.a,y.b);
	rt=merge(x.a,x.b);
}

查詢系列操作:

int Rank(int k)
{
	int res=0;
	pi x=split(rt,k);
	res=siz[x.a]+1;
	merge(x.a,x.b);
	return res;
}
int kth(int x,int k)
{
	if(k==siz[ch[x][0]]+1)return val[x];
	if(k<=siz[ch[x][0]])return kth(ch[x][0],k);
	return kth(ch[x][1],k-(siz[ch[x][0]]+1));
}
int pre(int x){int k=Rank(x)-1;return kth(rt,k);};
int suf(int x){int k=Rank(x+1);return kth(rt,k);};

然後這題就做完了

Code:

#include<bits/stdc++.h>
#include<time.h>
#include<ctime>
const int N=1.2e6+5;
using namespace std;
int n,m,cnt,rt;
int siz[N],val[N],pri[N];
int ch[N][2];
struct pi{
	int a,b;
	pi(int a_=0,int b_=0)
	{
		a=a_,b=b_;
	}
};
void upd(int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;};
pi split(int x,int k)
{
	if(!x)return pi(0,0);
	if(val[x]<k)//完全在右子樹 
	{
		pi y=split(ch[x][1],k);
		ch[x][1]=y.a;//將分裂後的左子樹與x合併(k不在y.a中) 
		upd(x);
		return pi(x,y.b);//最後分裂出了 x  y.b 返回這兩棵樹 
	}
	else//存在於左子樹內 
	{
		pi y= split(ch[x][0],k);
		ch[x][0]=y.b;
		upd(x);
		return pi(y.a,x);
	} 
}
int merge(int x,int y)
{
	if(!x||!y)return x+y;
	if(pri[x]<pri[y])//有點左偏樹的味道 
	{
		ch[x][1]=merge(ch[x][1],y);
		upd(x);
		return x;
	}
	else
	{
		ch[y][0]=merge(x,ch[y][0]);//始終滿足y子樹內任意值比x大
		upd(y);
		return y; 
	}
}
void insert(int k)
{
	val[++cnt]=k;pri[cnt]=rand()*5+rand();siz[cnt]=1;
	pi x=split(rt,k);
	rt=merge(merge(x.a,cnt),x.b);
}
void del(int k)
{
	pi x,y;
	x=split(rt,k);//x.a:[0,k-1] x.b:[k,inf] 
	y=split(x.b,k+1);//y.a[k,k] y.b:[k+1,inf]
	y.a=merge(ch[y.a][0],ch[y.a][1]);
	x.b=merge(y.a,y.b);
	rt=merge(x.a,x.b);
}
int Rank(int k)
{
	int res=0;
	pi x=split(rt,k);
	res=siz[x.a]+1;
	merge(x.a,x.b);
	return res;
}
int kth(int x,int k)
{
	if(k==siz[ch[x][0]]+1)return val[x];
	if(k<=siz[ch[x][0]])return kth(ch[x][0],k);
	return kth(ch[x][1],k-(siz[ch[x][0]]+1));
}
int pre(int x){int k=Rank(x)-1;return kth(rt,k);};
int suf(int x){int k=Rank(x+1);return kth(rt,k);};
int main()
{
	//freopen("P6136.in","r",stdin);
	cin>>n>>m;
	srand(clock());
	for(int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		insert(x);
	}
	int ans=0,last=0;
	for(int i=1,opt,x;i<=m;i++)
	{
		scanf("%d%d",&opt,&x);
		x^=last;
		switch(opt)
		{
			case 1: insert(x);break;
			case 2: del(x);break;
			case 3: last=Rank(x);ans^=last;break;
			case 4: last=kth(rt,x);ans^=last;break;
			case 5: last=pre(x);ans^=last;break;
			case 6: last=suf(x);ans^=last;break;
		}
	}
	printf("%d",ans);
}

相關文章