FHQ-Treap
全码
优点:码量短
写错了的话,\(TLE,MLE,Wa\)全家桶
包含合并操作
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
#define pii pair<int,int>
#define ull unsigned long long
#define pb push_back
#define ts cout<<"----------------"<<endl;
#define bs bitset<65>
using namespace std;
const int N = 1e6+5;
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
int rand()
{
uniform_int_distribution<ll>rg(LONG_LONG_MIN,LONG_LONG_MAX);
return rg(rnd)%INT_MAX;
}
struct FHQ_Treap
{
int ch[N][2],sz[N],rt,tot,id[N],v[N];
inline void pushup(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}
inline int newnode(int x)
{
sz[++tot]=1;v[tot]=x;id[tot]=rand();
return tot;
}
inline int merge(int x,int y)
{
if(!x||!y)return x^y;
if(id[x]<id[y])return ch[x][1]=merge(ch[x][1],y),pushup(x),x;
return ch[y][0]=merge(x,ch[y][0]),pushup(y),y;
}
inline void split(int now,int k,int &x,int &y)
{
if(!now)x=0,y=0;
else
{
if(k>=v[now])x=now,split(ch[now][1],k,ch[now][1],y);
else y=now,split(ch[now][0],k,x,ch[now][0]);
pushup(now);
}
}
inline void insert(int k)
{
int x,y;
split(rt,k,x,y);
rt=merge(merge(x,newnode(k)),y);
}
inline void del(int k)
{
int x,y,z;
split(rt,k,x,y);
split(x,k-1,x,z);//attention is x not rt
z=merge(ch[z][0],ch[z][1]);rt=merge(merge(x,z),y);
}
inline int kth(int now,int k)
{
while(1)
{
if(k<=sz[ch[now][0]])now=ch[now][0];
else if(k==sz[ch[now][0]]+1)return now;
else k-=sz[ch[now][0]]+1,now=ch[now][1];
}
}
inline int rank(int k)
{
int x,y;
split(rt,k-1,x,y);int ans=sz[x]+1;
return rt=merge(x,y),ans;
}
inline int pre(int k)
{
int x,y,ans;
split(rt,k-1,x,y);
ans=v[kth(x,sz[x])];
return rt=merge(x,y),ans;
}
inline int nxt(int k)
{
int x,y,ans;
split(rt,k,x,y);
ans=v[kth(y,1)];
return rt=merge(x,y),ans;
}
}tree;
int n;
int main()
{
speed();
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
cin>>n;
int opt,x;
for(int i=1;i<=n;i++)
{
cin>>opt>>x;
if(opt==1)
{
tree.insert(x);
}else if(opt==2)
{
tree.del(x);
}else if(opt==3)
{
cout<<tree.rank(x)<<endl;
}else if(opt==4)
{
cout<<tree.v[tree.kth(tree.rt,x)]<<endl;
}else if(opt==5)
{
cout<<tree.pre(x)<<endl;
}else
{
cout<<tree.nxt(x)<<endl;
}
}
return 0;
}
核心函数是合并和分离,所以都是基于\(rand\)维护
合并
格外要注意的是合并\(x\)和\(y\)要保证\(x\)中的所有元素都小于\(y\)中的,这里采用\(id\)小的在上面
int merge(int x,int y)
{
if(!x||!y)return x^y;//返回非0项
if(id[x]<id[y])return ch[x][1]=merge(ch[x][1],y),up(x),x;
return ch[y][0]=merge(x,ch[y][0]),up(y),y;
}
分裂操作,递归分裂,注意细节
注意\(x\)包含\(k\),\(y\)不包含\(k\)
inline void split(int now,int k,int &x,int &y)
{
if(!now)x=0,y=0;
else
{
if(k>=v[now])x=now,split(ch[now][1],k,ch[now][1],y);//一定要记得更新x,y
else y=now,split(ch[now][0],k,x,ch[now][0]);//一定要记得更新x,y
pushup(now);
}
}
插入,删除
inline void insert(int k)
{
int x,y;
split(rt,k,x,y);
rt=merge(merge(x,newnode(k)),y);
}
inline void del(int k)
{
int x,y,z;
split(rt,k,x,y);
split(x,k-1,x,z);//attention is x not rt
z=merge(ch[z][0],ch[z][1]);rt=merge(merge(x,z),y);
}
查询第\(k\)小
inline int kth(int now,int k)
{
while(1)
{
if(k<=sz[ch[now][0]])now=ch[now][0];
else if(k==sz[ch[now][0]]+1)return now;
else k-=sz[ch[now][0]]+1,now=ch[now][1];
}
}
查询权值的排名
inline int rank(int k)
{
int x,y;
split(rt,k-1,x,y);int ans=sz[x]+1;
return rt=merge(x,y),ans;
}
查询前驱,后继
inline int pre(int k)
{
int x,y,ans;
split(rt,k-1,x,y);
ans=v[kth(x,sz[x])];
return rt=merge(x,y),ans;
}
inline int nxt(int k)
{
int x,y,ans;
split(rt,k,x,y);
ans=v[kth(y,1)];
return rt=merge(x,y),ans;
}
文艺平衡树
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
#define pii pair<int,int>
#define ull unsigned long long
#define pb push_back
#define ts cout<<"----------------"<<endl;
#define bs bitset<65>
using namespace std;
const int N = 1e6+5;
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
int rand()
{
uniform_int_distribution<ll>rg(LONG_LONG_MIN,LONG_LONG_MAX);
return rg(rnd)%INT_MAX;
}
struct FHQ_Treap
{
int ch[N][2],tag[N],sz[N],v[N],id[N],tot,rt;
void pushup(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}
inline void lazytag(int x)
{
swap(ch[x][0],ch[x][1]);
tag[ch[x][0]]^=1;tag[ch[x][1]]^=1;
}
inline void pushdown(int x)
{
if(!tag[x])return;
swap(ch[x][0],ch[x][1]);
lazytag(ch[x][0]);lazytag(ch[x][1]);tag[x]=0;
}
inline int newnode(int x)
{
tag[++tot]=0;id[tot]=rand();sz[tot]=1;
v[tot]=x;return tot;
}
inline void split(int now,int k,int &x,int &y)
{
if(!now)x=0,y=0;
else
{
pushdown(now);
if(k>sz[ch[now][0]])x=now,split(ch[now][1],k-sz[ch[now][0]]-1,ch[now][1],y);
else y=now,split(ch[now][0],k,x,ch[now][0]);
pushup(now);
}
}
inline int merge(int x,int y)
{
if(!x||!y)return x^y;
if(id[x]<id[y])return pushdown(x),ch[x][1]=merge(ch[x][1],y),pushup(x),x;
return pushdown(y),ch[y][0]=merge(x,ch[y][0]),pushup(y),y;
}
inline void insert(int k)
{
int x,y;
split(rt,k,x,y);
rt=merge(merge(x,newnode(k)),y);
}
inline void reverse(int l,int r)
{
int x,y,z;
split(rt,r,x,y);
split(x,l-1,x,z);
lazytag(z);
rt=merge(merge(x,z),y);
}
inline void print(int u)
{
if(!u)return;
pushdown(u);
if(ch[u][0])print(ch[u][0]);
cout<<v[u]<<" ";
if(ch[u][1])print(ch[u][1]);
}
}tree;
int n,m;
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
cin>>n>>m;int l,r;
for(int i=1;i<=n;i++)tree.insert(i);
while(m--)
{
cin>>l>>r;
tree.reverse(l,r);
}
tree.print(tree.rt);
return 0;
}
关于\(split\)操作,为什么要用\(k\)和\(siz\)比较,因为此时维护的是翻转过的序列,已经不是按照大小关系排的二叉树了,而是按照\(siz\)
区间修改操作
例题
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
#define pb push_back
#define ull unsigned long long
#define int long long
using namespace std;
#define ts cout<<"****************"<<endl;
const int N =2e5+1000,mod=1e6;
const ull B= 233;
int sz[N],ch[N][2],id[N],tot,rt;
int n;
mt19937 seed(chrono::system_clock::now().time_since_epoch().count());
int Rand()
{
uniform_int_distribution<int>rang(1,INT_MAX);
return rang(seed);
}
inline void write(int x)
{
return x<0?(putchar_unlocked('-'),write(-x),void(0)):(x==0?void(0):(write(x/10),putchar_unlocked((x%10)|48),void(0)));
}
inline int read()
{
int x=0,f=1;char ch=getchar_unlocked();
for(;ch<'0'||ch>'9';ch=getchar_unlocked())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar_unlocked())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
int a[N],m;char s[N];
int tur[N],add[N],maxn[N];
struct FHQ
{
inline void pushup(int x)
{
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
maxn[x]=a[x];
if(ch[x][0])
maxn[x]=max(maxn[x],maxn[ch[x][0]]);
if(ch[x][1])
maxn[x]=max(maxn[x],maxn[ch[x][1]]);
// cout<<maxn[x]<<" "<<x<<endl;
}
inline void pushdown(int x)
{
if(tur[x])
{
if(ch[x][0])tur[ch[x][0]]^=1;
if(ch[x][1])tur[ch[x][1]]^=1;
// swap(ch[x][0],ch[x][1]);
ch[x][0]^=ch[x][1]^=ch[x][0]^=ch[x][1];
tur[x]=0;
}
if(add[x])
{
int v=add[x];
if(ch[x][0])
{
add[ch[x][0]]+=v;
maxn[ch[x][0]]+=v;
a[ch[x][0]]+=v;
}
if(ch[x][1])
{
add[ch[x][1]]+=v;
maxn[ch[x][1]]+=v;
a[ch[x][1]]+=v;
}
add[x]=0;
}
}
inline int newnode(int x)
{
// cout<<x<<endl;
a[++tot]=0;maxn[tot]=0;
id[tot]=rand();sz[tot]=1;return tot;
}
inline void split(int now,int k,int &x,int &y)
{
if(!now)x=0,y=0;
else
{
pushdown(now);
if(k>sz[ch[now][0]])x=now,split(ch[now][1],k-sz[ch[now][0]]-1,ch[x][1],y);
else y=now,split(ch[now][0],k,x,ch[y][0]);
pushup(now);
}
}
inline int merge(int x,int y)
{
if(x)pushdown(x);
if(y)pushdown(y);
if(!x||!y)return x^y;
if(id[x]<id[y])return ch[x][1]=merge(ch[x][1],y),pushup(x),x;
return ch[y][0]=merge(x,ch[y][0]),pushup(y),y;
}
inline void insert(int k)
{
int x,y;
split(rt,k,x,y);
// cout<<pos<<" "<<y<<endl;
rt=merge(merge(x,newnode(k)),y);
}
inline void del(int k)
{
int x,y,z;
split(rt,k,x,y);
split(x,k-1,x,z);
z=merge(ch[z][0],ch[z][1]);
rt=merge(merge(x,z),y);
}
inline void update1(int l,int r,int val)
{
int x,y,z;
split(rt,r,x,y);
split(x,l-1,x,z);
maxn[z]+=val;
a[z]+=val;
add[z]+=val;
rt=merge(merge(x,z),y);
}
inline void update2(int l,int r)
{
int x,y,z;
split(rt,r,x,y);
split(x,l-1,x,z);
tur[z]^=1;
rt=merge(merge(x,z),y);
}
inline void query(int l,int r)
{
int x,y,z;
split(rt,r,x,y);
split(x,l-1,x,z);
// cout<<z<<" "<<l<<" "<<r<<endl;
maxn[z]?write(maxn[z]):(putchar_unlocked('0'),void(0));
putchar_unlocked('\n');
rt=merge(merge(x,z),y);
}
}tree;
signed main()
{
// freopen("in.in","r",stdin);freopen("ou1.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)
tree.insert(0);
int com,l,r,val;
while(m--) {
com=read();
if(com==1) {
l=read();r=read();val=read();
tree.update1(l,r,val);
}
if(com==2) {
l=read();r=read();
tree.update2(l,r);
}
if(com==3) {
l=read();r=read();
tree.query(l,r);
}
}
return 0;
}
\(FHQ\)的值域有交集,如何合并,这时候\(merge\)就不管用了,\(merge\)是要保证\(x\)中的元素都小于\(y\)中的元素,这时候只能可以递归分裂后合并
详见文章
inline int join(int x,int y)
{
if(!x||!y)return x+y;
if(id[x]>id[y])swap(x,y);
int l,r;
split(y,val[x],l,r);
ch[x][0]=join(ch[x][0],l);ch[x][1]=join(ch[x][1],r);
pushup(x);
return x;
}
疑问,下面两个\(kth\)为什么注释部分不对
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define pb push_back
#define ull unsigned long long
#define pii pair<int,int>
#define lid (ch[rt][0])
#define rid (ch[rt][1])
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
#define ts cout<<"************"<<endl;
using namespace std;
const int N = 2e6+5,INF=1E9,mod=1e9+7;
inline int read()
{
int x=0,f=1;char ch=getchar_unlocked();
for(;ch<'0'||ch>'9';ch=getchar_unlocked())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar_unlocked())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
inline void write(ll x)
{
if(x<0)x=-x,putchar_unlocked('-');
if(x>9)write(x/10);
putchar_unlocked((x%10)|48);
}
int n;
std::vector<int> edge[N];
int fa[N];
inline void dfs(int u)
{
for(auto to:edge[u])
{
if(to==fa[u])continue;
fa[to]=u;
dfs(to);
}
}
ll sum[N],ans[N];
mt19937 rnd (chrono::system_clock::now().time_since_epoch().count());
int lim;
inline int rd()
{
uniform_int_distribution<int>rg(1,INT_MAX);
return rg(rnd);
}
#define int long long
int sz[N],ch[N][2],id[N],tot,cnt[N];ll val[N];int rt[N];
struct FHQ
{
inline void pushup(int rt)
{
sz[rt]=sz[lid]+sz[rid]+cnt[rt];
}
inline int newnode(int c,int s)
{
val[++tot]=c;cnt[tot]=s;sz[tot]=s;id[tot]=rd();
return tot;
}
inline int merge(int x,int y)
{
if(!x||!y)return x|y;
if(id[x]<id[y])return ch[x][1]=merge(ch[x][1],y),pushup(x),x;
return ch[y][0]=merge(x,ch[y][0]),pushup(y),y;
}
inline void split(int now,int k,int &x,int &y)
{
if(!now)x=y=0;
else
{
if(k>=val[now])x=now,split(ch[now][1],k,ch[now][1],y);
else y=now,split(ch[now][0],k,x,ch[now][0]);
pushup(now);
}
}
inline void insert(int &rt,int c,int s)
{
int x,y;
split(rt,c,x,y);
rt=merge(merge(x,newnode(c,s)),y);
}
inline int join(int x,int y)
{
if(!x||!y)return x+y;
if(id[x]>id[y])swap(x,y);
int l,r;
split(y,val[x],l,r);
ch[x][0]=join(ch[x][0],l);
ch[x][1]=join(ch[x][1],r);
pushup(x);
return x;
}
inline void move(int &x,int &y,int l,int r)
{
int a,b,c;
split(x,l-1,a,b);
split(b,r,b,c);
y=b;
x=merge(a,c);
}
inline int count(int &u,int l,int r)
{
int x,y,z;
split(u,l-1,x,y);
split(y,r,y,z);
int ans=sz[y];
u=merge(merge(x,y),z);
return ans;
}
void split_rk(int u,int c,int &x,int &y){
if(!u){
x = y = 0;
return;
}
if(sz[ch[u][0]]+cnt[u]<=c){
x = u;
split_rk(ch[u][1],c-sz[ch[u][0]]-cnt[u],ch[x][1],y);
}
else{
y = u;
split_rk(ch[u][0],c,x,ch[y][0]);
}
pushup(u);
}
int kth(int &u,int K){
int x,y;
split_rk(u,K-1,x,y);
int p = y;
while(ch[p][0])
p = ch[p][0];
u = merge(x,y);
return p?val[p]:-1;
}
// inline int kth(int rt,int k)
// {
// if(sz[rt]<k)return -1;
// int now=rt;
// while(1)
// {
// if(sz[ch[now][0]]+cnt[now]<=k&&k>sz[ch[now][0]])return val[now];
// if(k>sz[ch[now][0]]+cnt[now])k-=sz[ch[now][0]]+cnt[now],k=ch[now][1];
// else if(k<=sz[ch[now][0]])
// {
// now=ch[now][0];
// }
// }
// }
}T;
int m,idx;
signed main(){
file("a");
n = read(),m = read();
for(int k=1;k<=n;k++)
T.insert(rt[1],k,read());
int idx = 1;
int op,p;
while(m--){
int op = read(),p = read();
if(!op){
int x = read(),y = read();
T.move(rt[p],rt[++idx],x,y);
}
else if(op==1){
int t = read();
rt[p] = T.join(rt[p],rt[t]);
}
else if(op==2){
int x = read(),q = read();
T.insert(rt[p],q,x);
}
else if(op==3){
int x = read(),y = read();
write(T.count(rt[p],x,y));
putchar_unlocked('\n');
}
else{
write(T.kth(rt[p],read()));
putchar_unlocked('\n');
}
}
return 0;
}