「some」FHQ-Treap
无旋 \(Treap\) ,也就是 \(FHQ-Treap\),与普通 \(Treap\) 相比,没有旋转操作,代之的是分裂 \(split\) 和合并 \(merge\) 两个核心操作,来实现其他操作,如求一个数的排名,第k大的数,前驱后继等。
还可以可持久化码量小,好打,好理解
存储的信息
int tot; // 节点个数
struct TREE
{
int size; //子树的大小
int ls,rs; // 左子树,右子树
int key,val; // 随机值,权值
}bst[MAX];
新建一个权值为 \(val\) 的节点
inline int new_pot(int val)
{
bst[++tot].val = val;
bst[tot].size = 1;
bst[tot].key = rand();
return tot;
}
\(Push\;up\)
合并子树大小。
inline void Push_up(int p)
{ bst[p].size = bst[bst[p].ls].size+bst[bst[p].rs].size+1; }
分裂 \(split\)
分为按权值分裂,和按排名分裂。
按权值分裂,将根节点为 \(p\) 的一棵树分成以 \(p1\) 为根节点的左子树和以 \(p2\) 为根节点的右子树。其中左子树中的所有节点都满足 \(\le val\) ,右子树的所有节点都满足 \(> val\)
inline void split(int p,int val,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[p].val<=val)
{ split(bst[p1 = p].rs,val,bst[p].rs,p2); }
else
{ split(bst[p2 = p].ls,val,p1,bst[p].ls); }
Push_up(p);
}
按排名分裂
跟按权值分裂差不多,依靠 \(size\) 实现。将前 \(k\) 个放一个树里,剩下的放一个树里。注意 \(size\) 并不是其排名大小。
碰上序列操作的问题,需要用到排名分裂。
inline void split(int p,int k,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[bst[p].ls].size>=k)
{ split(bst[p2 = p].ls,k,p1,bst[p].ls); }
else
{ spit(bst[p1 = p].rs,k-bst[bst[p].ls].size-1,bst[p].rs,p2); }
Push_up(p);
}
合并 \(merge\)
将以 \(p1,p2\) 为根节点的两颗树合并,并返回合并后的根节点
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
if(bst[p1].key<bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls = merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
插入 \(insert\)
先按 \(val\) 分裂成 \(p1,p2\) ,再将 \(p1\) 和新节点合并,最后合并 \(p2\) 。注意合并顺序。
inline void insert(int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
root = merge(merge(p1,new_pot(val)),p2);
}
删除 \(del\)
先将原树按 \(val\) 分裂成 \(p1,p3\) ,再按 \(val-1\) 将 \(p1\) 分裂成 \(p1,p2\) ,删去 \(p2\) 的根节点即可,最后将 \(p1,p2,p3\) 合并起来
inline void del(int val)
{
int p1 = 0,p2 = 0,p3 = 0;
split(root,val,p1,p3),split(p1,val-1,p1,p2);
p2 = merge(bst[p2].ls,bst[p2].rs);
root = merge(merge(p1,p2),p3);
}
查询排名
将原树按 \(val-1\) 分裂成 \(p1,p2\) ,\(val\) 的排名即为 \(p1\) 的大小。注意,先记录答案,再 \(merge\) ,先 \(merge\) 可能会改变 \(p1\) 的大小。
inline int rank(int val)
{
int p1 = 0,p2 = 0;
split(root,val-1,p1,p2);
int ans = bst[p1].size+1;
root = merge(p1,p2);
return ans;
}
查询排名为 \(k\) 的数
从根节点开始,去左子树中查找,如果左子树大小比当前的 \(k\) 大,则说明排名为 \(k\) 的数在其左子树中,如果小,则去右子树中找,同时 \(k-=bst[bst[p].ls]size+1\) 。注意此处的排名是按升序排的。
inline int kth(int k)
{
int p = root;
while(1)
{
if(k==bst[bst[p].ls].size+1)
{ return bst[p].val; }
if(bst[bst[p].ls].size>=k)
{ p = bst[p].ls; }
else
{ k -= bst[bst[p].ls].size+1; p = bst[p].rs; }
}
}
查询前驱
前驱定义为小于 \(val\) ,且最大的数。
将原树按 \(val\) 分裂成 \(p1,p2\) ,去找 \(p1\) 的最大值,即为 \(val\) 的前驱。
inline int pre(int val)
{
int p1 = 0,p2 = 0;
split(root,val-1,p1,p2);
int p = p1;
while(bst[p].rs)
{ p = bst[p].rs; }
root = merge(p1,p2);
return bst[p].val;
}
查询后继
后继定义为大于 \(val\) ,且最小的数。
将原树按 \(val\) 分裂为 \(p1,p2\) ,去找 \(p2\) 的最小值,即为 \(val\) 的后继。
inline int suf(int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
int p = p2;
while(bst[p].ls)
{ p = bst[p].ls; }
root = merge(p1,p2);
return bst[p].val;
}
板子大融合
Code
#include<cstdio>
#include<time.h>
#include<stdlib.h>
#define MAX 100001
#define re register
namespace OMA
{
struct FHQ_Treap
{
struct TREE
{
int size;
int ls,rs;
int val,key;
}bst[MAX];
int tot,root;
inline int new_pot(int val)
{
bst[++tot].val = val;
bst[tot].size = 1;
bst[tot].key = rand();
return tot;
}
inline void Push_up(int x)
{ bst[x].size = bst[bst[x].ls].size+bst[bst[x].rs].size+1; }
inline void split(int p,int val,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[p].val<=val)
{ split(bst[p1 = p].rs,val,bst[p].rs,p2); }
else
{ split(bst[p2 = p].ls,val,p1,bst[p].ls); }
Push_up(p);
}
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
if(bst[p1].key>bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls = merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
inline void insert(int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
root = merge(merge(p1,new_pot(val)),p2);
}
inline void del(int val)
{
int p1 = 0,p2 = 0,p3 = 0;
split(root,val,p1,p3),split(p1,val-1,p1,p2);
p2 = merge(bst[p2].ls,bst[p2].rs);
root = merge(merge(p1,p2),p3);
}
inline int rank(int val)
{
int p1 = 0,p2 = 0;
split(root,val-1,p1,p2);
int ans = bst[p1].size+1;
root = merge(p1,p2);
return ans;
}
inline int kth(int x)
{
int p = root;
while(1)
{
if(x==bst[bst[p].ls].size+1)
{ return bst[p].val; }
if(bst[bst[p].ls].size>=x)
{ p = bst[p].ls; }
else
{ x -= bst[bst[p].ls].size+1,p = bst[p].rs; }
}
}
inline int pre(int val)
{
int p1 = 0,p2 = 0;
split(root,val-1,p1,p2);
int p = p1;
while(bst[p].rs)
{ p = bst[p].rs; }
root = merge(p1,p2);
return bst[p].val;
}
inline int suf(int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
int p = p2;
while(bst[p].ls)
{ p = bst[p].ls; }
root = merge(p1,p2);
return bst[p].val;
}
}Tree;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
int n,opt,x;
signed main()
{
srand(time(NULL));
n = read();
for(re int i=1; i<=n; i++)
{
opt = read(),x = read();
if(opt==1)
{ Tree.insert(x); }
if(opt==2)
{ Tree.del(x); }
if(opt==3)
{ printf("%d\n",Tree.rank(x)); }
if(opt==4)
{ printf("%d\n",Tree.kth(x)); }
if(opt==5)
{ printf("%d\n",Tree.pre(x)); }
if(opt==6)
{ printf("%d\n",Tree.suf(x)); }
}
return 0;
}
}
signed main()
{ return OMA::main(); }
序列操作,所以用排名分裂。外加下传懒标记。
将原序列分裂为 \([1,l-1],[l,r],[r+1,n]\),分别对应 \(p1,p2,p3\) ,对 \(p2\) 的懒标记进行修改就好,合并时下放懒标记。最后 \(dfs\) 输出答案。
Code
#include<time.h>
#include<cstdio>
#include<stdlib.h>
#define MAX 100001
#define re register
namespace OMA
{
int n,m;
int root;
struct FHQ_Treap
{
int tot;
struct TREE
{
int val;
int size;
int ls,rs;
int tag,key;
}bst[MAX];
inline int new_pot(int val)
{
bst[++tot].val = val;
bst[tot].size = 1;
bst[tot].key = rand();
return tot;
}
inline void Push_up(int x)
{ bst[x].size = bst[bst[x].ls].size+bst[bst[x].rs].size+1; }
inline void swap(int &a,int &b)
{ int t=a; a=b,b=t; }
inline void Push_down(int x)
{
if(bst[x].tag)
{
bst[x].tag = 0;
swap(bst[x].ls,bst[x].rs);
bst[bst[x].ls].tag ^= 1,bst[bst[x].rs].tag ^= 1;
}
}
inline void split(int p,int size,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
Push_down(p);
if(bst[bst[p].ls].size>=size)
{ split(bst[p2 = p].ls,size,p1,bst[p].ls); }
else
{ split(bst[p1 = p].rs,size-bst[bst[p].ls].size-1,bst[p].rs,p2); }
Push_up(p);
}
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
Push_down(p1),Push_down(p2);
if(bst[p1].key<bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls = merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
inline void ans(int p)
{
if(!p)
{ return ; }
Push_down(p);
ans(bst[p].ls);
printf("%d ",bst[p].val);
ans(bst[p].rs);
}
}Tree;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
signed main()
{
srand(time(NULL));
n = read(),m = read();
for(re int i=1; i<=n; i++)
{ root = Tree.merge(root,Tree.new_pot(i)); }
for(re int i=1; i<=m; i++)
{
int p1,p2,p3;
int l = read(),r = read();
Tree.split(root,r,p1,p3),Tree.split(p1,l-1,p1,p2);
Tree.bst[p2].tag ^= 1;
root = Tree.merge(Tree.merge(p1,p2),p3);
}
Tree.ans(root);
return 0;
}
}
signed main()
{ return OMA::main(); }
树套树板子。
以下内容摘自 OI-WiKi 线段树套平衡树
关于树套树的构建,我们对于外层线段树正常建树,对于线段树上的某一个节点,建立一棵平衡树,包含该节点所覆盖的序列。具体操作时我们可以将序列元素一个个插入,每经过一个线段树节点,就将该元素加入到该节点的平衡树中。
操作一,求某区间中某值的排名:我们对于外层线段树正常操作,对于在某区间中的节点的平衡树,我们返回平衡树中比该值小的元素个数,合并区间时,我们将小的元素个数求和即可。最后将返回值 \(+1\) ,即为某值在某区间中的排名。
操作二,求某区间中排名为 \(k\) 的值:我们可以采用二分策略。因为一个元素可能存在多个,其排名为一区间,且有些元素原序列不存在。所以我们采取和操作一类似的思路,我们用小于该值的元素个数作为参考进行二分,即可得解。
操作三,将某个数替换为另外一个数:我们只要在所有包含某数的平衡树中删除某数,然后再插入另外一个数即可。外层依旧正常线段树操作。
操作四,五,求某区间中某值的前驱(后继):我们对于外层线段树正常操作,对于在某区间中的节点的平衡树,我们返回某值在该平衡树中的前驱(后继),线段树的区间结果合并时,我们取最大(最小)值即可。
Code
#include<time.h>
#include<cstdio>
#include<stdlib.h>
#define MAX 50001
#define re register
#define TOP 2147483647
namespace OMA
{
int n,m,a[MAX];
struct Segment_Tree
{
struct TREE
{ int l,r,rt; };
TREE tree[MAX<<2];
struct FHQ_Treap
{
int tot;
struct TREE
{
int size;
int ls,rs;
int key,val;
}bst[MAX<<5];
inline int new_pot(int val)
{
bst[++tot].val = val;
bst[tot].size = 1;
bst[tot].key = rand();
return tot;
}
inline void Push_up(int p)
{ bst[p].size = bst[bst[p].ls].size+bst[bst[p].rs].size+1; }
inline void split(int p,int val,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[p].val<=val)
{ split(bst[p1 = p].rs,val,bst[p].rs,p2); }
else
{ split(bst[p2 = p].ls,val,p1,bst[p].ls); }
Push_up(p);
}
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
if(bst[p1].key>bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls = merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
inline void insert(int &root,int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
root = merge(merge(p1,new_pot(val)),p2);
}
inline void del(int &root,int val)
{
int p1 = 0,p2 = 0,p3 = 0;
split(root,val,p1,p3),split(p1,val-1,p1,p2);
p2 = merge(bst[p2].ls,bst[p2].rs);
root = merge(merge(p1,p2),p3);
}
inline int rank(int &root,int val)
{
int p1 = 0,p2 = 0;
split(root,val-1,p1,p2);
int ans = bst[p1].size;
root = merge(p1,p2);
return ans;
}
inline int pre(int &root,int val)
{
int p1 = 0,p2 = 0;
split(root,val-1,p1,p2);
if(!p1)
{ return -TOP; }
int p = p1;
while(bst[p].rs)
{ p = bst[p].rs; }
root = merge(p1,p2);
return bst[p].val;
}
inline int suf(int &root,int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
if(!p2)
{ return TOP; }
int p = p2;
while(bst[p].ls)
{ p = bst[p].ls; }
root = merge(p1,p2);
return bst[p].val;
}
}Treap;
inline int ls(int p)
{ return p<<1; }
inline int rs(int p)
{ return p<<1|1; }
inline void build(int p,int l,int r)
{
tree[p].l = l,tree[p].r = r;
if(l==r)
{ tree[p].rt = Treap.new_pot(a[l]); return ; }
for(re int i=l; i<=r; i++)
{ Treap.insert(tree[p].rt,a[i]); }
int mid = (l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
}
inline int Rank(int p,int l,int r,int val)
{
if(l<=tree[p].l&&tree[p].r<=r)
{ return Treap.rank(tree[p].rt,val); }
int ans = 0,mid = (tree[p].l+tree[p].r)>>1;
if(l<=mid)
{ ans += Rank(ls(p),l,r,val); }
if(r>mid)
{ ans += Rank(rs(p),l,r,val); }
return ans;
}
inline int Kth(int l,int r,int k)
{
int L = 0,R = 1e8,ans = 0;
while(L<=R)
{
int mid = (L+R)>>1;
if(Rank(1,l,r,mid)+1<=k)
{ ans = mid,L = mid+1; }
else
{ R = mid-1; }
}
return ans;
}
inline void change(int p,int pos,int val)
{
Treap.del(tree[p].rt,a[pos]);
Treap.insert(tree[p].rt,val);
if(tree[p].l==tree[p].r)
{ return ; }
int mid = (tree[p].l+tree[p].r)>>1;
if(pos<=mid)
{ change(ls(p),pos,val); }
if(pos>mid)
{ change(rs(p),pos,val); }
}
inline int max(int a,int b)
{ return a>b?a:b; }
inline int Pre(int p,int l,int r,int val)
{
if(l<=tree[p].l&&tree[p].r<=r)
{ return Treap.pre(tree[p].rt,val); }
int ans = -TOP,mid = (tree[p].l+tree[p].r)>>1;
if(l<=mid)
{ ans = max(ans,Pre(ls(p),l,r,val)); }
if(r>mid)
{ ans = max(ans,Pre(rs(p),l,r,val)); }
return ans;
}
inline int min(int a,int b)
{ return a<b?a:b; }
inline int Suf(int p,int l,int r,int val)
{
if(l<=tree[p].l&&tree[p].r<=r)
{ return Treap.suf(tree[p].rt,val); }
int ans = TOP,mid = (tree[p].l+tree[p].r)>>1;
if(l<=mid)
{ ans = min(ans,Suf(ls(p),l,r,val)); }
if(r>mid)
{ ans = min(ans,Suf(rs(p),l,r,val)); }
return ans;
}
}Tree;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
int opt,l,r;
signed main()
{
srand(time(NULL));
n = read(),m = read();
for(re int i=1; i<=n; i++)
{ a[i] = read(); }
Tree.build(1,1,n);
for(re int i=1; i<=m; i++)
{
opt = read(),l = read(),r = read();
if(opt==1)
{ printf("%d\n", Tree.Rank(1,l,r,read())+1); }
if(opt==2)
{ printf("%d\n",Tree.Kth(l,r,read())); }
if(opt==3)
{ Tree.change(1,l,r); a[l] = r; }
if(opt==4)
{ printf("%d\n", Tree.Pre(1,l,r,read())); }
if(opt==5)
{ printf("%d\n", Tree.Suf(1,l,r,read())); }
}
return 0;
}
}
signed main()
{ return OMA::main(); }
查询前驱后继板子,一开始插入极大值和极小值,同时注意此处的前驱后继并非严格意义上的前驱后继,可以相等。
跟上一道差不多。开两颗 \(Treap\) ,一个存宠物,一个存领养者,记得特判无前驱后继的情况,直接跳过就好,然后就是裸的查询前驱后继了。
Code
#include<time.h>
#include<cstdio>
#include<stdlib.h>
#define MAX 80001
#define re register
namespace OMA
{
struct FHQ_Treap
{
int tot;
int root;
struct TREE
{
int size;
int ls,rs;
int key,val;
}bst[MAX];
inline int new_pot(int val)
{
bst[++tot].val = val;
bst[tot].size = 1;
bst[tot].key = rand();
return tot;
}
inline void Push_up(int p)
{ bst[p].size = bst[bst[p].ls].size+bst[bst[p].rs].size+1; }
inline void split(int p,int val,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[p].val<=val)
{ split(bst[p1 = p].rs,val,bst[p].rs,p2); }
else
{ split(bst[p2 = p].ls,val,p1,bst[p].ls); }
Push_up(p);
}
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
if(bst[p1].key<bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls = merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
inline void insert(int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
root = merge(merge(p1,new_pot(val)),p2);
}
inline void del(int val)
{
int p1 = 0,p2 = 0,p3 = 0;
split(root,val,p1,p3),split(p1,val-1,p1,p2);
p2 = merge(bst[p2].ls,bst[p2].rs);
root = merge(merge(p1,p2),p3);
}
inline int pre(int val)
{
int p1 = 0,p2 = 0;
split(root,val-1,p1,p2);
int p = p1;
while(bst[p].rs)
{ p = bst[p].rs; }
root = merge(p1,p2);
return bst[p].val;
}
inline int suf(int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
int p = p2;
while(bst[p].ls)
{ p = bst[p].ls; }
root = merge(p1,p2);
return bst[p].val;
}
}Treap[2];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
int n,ans,cnt[2];
const int p = 1000000;
inline int min(int a,int b)
{ return a<b?a:b; }
inline int abs(int a)
{ return a>=0?a:-a; }
signed main()
{
n = read();
Treap[0].insert(0x7f7f7f7f),Treap[0].insert(-0x7f7f7f7f);
Treap[1].insert(0x7f7f7f7f),Treap[1].insert(-0x7f7f7f7f);
for(re int i=1; i<=n; i++)
{
int a = read(),b = read();
cnt[a]++;
Treap[a].insert(b);
if(cnt[a^1]>0)
{
int pre = Treap[a^1].pre(b),suf = Treap[a^1].suf(b);
if(pre==-0x7f7f7f7f&&suf==0x07f7f7f7f)
{ continue; }
int temp = (abs(b-pre)<abs(b-suf))?pre:((abs(b-pre)==abs(b-suf))?((pre<suf)?pre:suf):suf);
(ans += abs(b-temp)) %= p;
Treap[a^1].del(temp);
Treap[a].del(b);
cnt[a^1]--;
}
}
printf("%d\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
暴力修改
-
\(I\) 操作,不小于 \(\min\) 直接插入,小于不用管。
-
\(A\) 操作,直接 \(dfs\) 一波,全加上 \(k\)。
-
\(S\) 操作,如果降工资后,有员工离职,则降工资之前,这些员工的工资,都满足小于 \(\min+k\) ,所以将原树按 \(\min+k-1\) 分裂成 \(p1,p2\) ,则,\(p1\) 的大小即为此次降工资后,离职员工的个数,也不用合并,毕竟离职了,直接扔掉就好
无良老板,然后再 \(dfs\) 一波,全减去 \(k\) 。 -
\(F\) 操作,板子是升序,此处是降序,原先的第 \(k\) 大,就成了第 \(bst[root].size-k+1\) 小。
Code
#include<time.h>
#include<cstdio>
#include<stdlib.h>
#define re register
#define MAX 100001
namespace OMA
{
int root;
int n,edge,ans;
struct FHQ_Treap
{
int tot;
struct TREE
{
int size;
int ls,rs;
int val,key;
}bst[MAX];
inline int new_pot(int val)
{
bst[++tot].val = val;
bst[tot].size = 1;
bst[tot].key = rand();
return tot;
}
inline void Push_up(int p)
{ bst[p].size = bst[bst[p].ls].size+bst[bst[p].rs].size+1; }
inline void split(int p,int val,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[p].val<=val)
{ split(bst[p1 = p].rs,val,bst[p].rs,p2); }
else
{ split(bst[p2 = p].ls,val,p1,bst[p].ls); }
Push_up(p);
}
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
if(bst[p1].key>bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls =merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
inline void insert(int val)
{
int p1 = 0,p2 = 0;
split(root,val,p1,p2);
root = merge(merge(p1,new_pot(val)),p2);
}
inline int kth(int k)
{
int p = root;
while(1)
{
if(k==bst[bst[p].ls].size+1)
{ return bst[p].val; }
if(bst[bst[p].ls].size>=k)
{ p = bst[p].ls; }
else
{ k -= bst[bst[p].ls].size+1; p = bst[p].rs; }
}
}
inline void dfs(int p,int delta)
{
if(!p)
{ return ; }
bst[p].val += delta;
dfs(bst[p].ls,delta),dfs(bst[p].rs,delta);
}
}Treap;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
signed main()
{
n = read(),edge = read();
for(re int i=1; i<=n; i++)
{
char opt[5];
scanf("%s",opt);
int k = read();
if(opt[0]=='I')
{
if(k>=edge)
{ Treap.insert(k); }
}
if(opt[0]=='A')
{ Treap.dfs(root,k); }
if(opt[0]=='S')
{
int p = 0;
Treap.split(root,edge+k-1,p,root);
ans += Treap.bst[p].size;
Treap.dfs(root,-k);
}
if(opt[0]=='F')
{
if(k>Treap.bst[root].size)
{ printf("-1\n"); continue ; }
printf("%d\n",Treap.kth(Treap.bst[root].size-k+1));
}
}
printf("%d\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
每个节点代表子树的哈希值。按位置分裂,这样就可以查询区间的哈希值了。
对于区间哈希值,则有
Push up 的时候维护一下就好。
不要忘了PushupQAQ
Code
#include<time.h>
#include<cstdio>
#include<stdlib.h>
#include<cstring>
#define MAX 2000010
#define re register
#define ull unsigned long long
namespace OMA
{
int root;
int m,len;
char ch[MAX];
ull bin[MAX] = {1};
const ull base = 131;
struct FHQ_Treap
{
int tot;
struct TREE
{
int size;
ull hash;
int ls,rs;
int val,key;
}bst[MAX];
inline int new_pot(int val)
{
bst[++tot].size = 1;
bst[tot].key = rand();
bst[tot].val = bst[tot].hash = val;
return tot;
}
inline void Push_up(int p)
{
bst[p].size = bst[bst[p].ls].size+bst[bst[p].rs].size+1;
bst[p].hash = bst[bst[p].rs].hash+bst[bst[p].ls].hash*bin[bst[bst[p].rs].size+1]+bst[p].val*bin[bst[bst[p].rs].size];
}
inline void split(int p,int pos,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[bst[p].ls].size>=pos)
{ split(bst[p2 = p].ls,pos,p1,bst[p].ls); }
else
{ split(bst[p1 = p].rs,pos-bst[bst[p].ls].size-1,bst[p].rs,p2); }
Push_up(p);
}
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
if(bst[p1].key<bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls = merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
inline void insert(int p,int val)
{
int p1 = 0,p2 = 0;
split(root,p,p1,p2);
root = merge(merge(p1,new_pot(val)),p2);
}
inline void del(int p)
{
int p1 = 0,p2 = 0,p3 =0;
split(root,p,p1,p3),split(p1,p-1,p1,p2);
p2 = merge(bst[p2].ls,bst[p2].rs);
root = merge(merge(p1,p2),p3);
}
inline ull check(int l,int r)
{
int p1 = 0,p2 = 0,p3 = 0;
split(root,r,p1,p3),split(p1,l-1,p1,p2);
ull ans = bst[p2].hash;
root = merge(merge(p1,p2),p3);
return ans;
}
inline int min(int a,int b)
{ return a<b?a:b; }
inline int query(int a,int b)
{
int l = 0,r = min(len-a+1,len-b+1),ans = 0;
while(l<=r)
{
int mid = (l+r)>>1;
if(check(a,a+mid-1)==check(b,b+mid-1))
{ l = mid+1,ans = mid; }
else
{ r = mid-1; }
}
return ans;
}
}Treap;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
signed main()
{
srand(time(NULL));
scanf("%s",ch+1);
m = read();
len = strlen(ch+1);
for(re int i=1; i<=MAX-1; i++)
{ bin[i] = bin[i-1]*base; }
for(re int i=1; i<=len; i++)
{ root = Treap.merge(root,Treap.new_pot(ch[i]-'a'+1)); }
for(re int i=1; i<=m; i++)
{
char opt[5];
scanf("%s",opt);
int a = read();
if(opt[0]=='Q')
{
int b = read();
printf("%d\n",Treap.query(a,b));
}
if(opt[0]=='R')
{
char b[5];
scanf("%s",b);
Treap.del(a),Treap.insert(a-1,b[0]-'a'+1);
}
if(opt[0]=='I')
{
len++;
char b[5];
scanf("%s",b);
Treap.insert(a,b[0]-'a'+1);
}
}
return 0;
}
}
signed main()
{ return OMA::main(); }
咕咕咕
Code
#include<time.h>
#include<cstdio>
#include<stdlib.h>
#define MAX 100001
#define re register
namespace OMA
{
int root;
struct FHQ_Treap
{
int tot;
struct TREE
{
int len;
int size;
int ls,rs;
int key,val;
}bst[MAX];
inline int max(int a,int b)
{ return a>b?a:b; }
inline void Push_up(int p)
{
bst[p].size = bst[bst[p].ls].size+bst[bst[p].rs].size+1;
bst[p].len = max(max(bst[bst[p].ls].len,bst[bst[p].rs].len),bst[p].val);
}
inline int new_pot(int val)
{
bst[++tot].size = 1;
bst[tot].key = rand();
bst[tot].val = bst[tot].len = val;
return tot;
}
inline void split(int p,int k,int &p1,int &p2)
{
if(!p)
{ p1 = p2 = 0; return ; }
if(bst[bst[p].ls].size>=k)
{ split(bst[p2 = p].ls,k,p1,bst[p].ls); }
else
{ split(bst[p1 = p].rs,k-bst[bst[p].ls].size-1,bst[p].rs,p2); }
Push_up(p);
}
inline int merge(int p1,int p2)
{
if(!p1||!p2)
{ return p1|p2; }
if(bst[p1].key<bst[p2].key)
{
bst[p1].rs = merge(bst[p1].rs,p2);
Push_up(p1);
return p1;
}
else
{
bst[p2].ls = merge(p1,bst[p2].ls);
Push_up(p2);
return p2;
}
}
inline void insert(int p,int val)
{
int p1 = 0,p2 = 0;
split(root,p,p1,p2);
root = merge(merge(p1,new_pot(bst[p1].len+1)),p2);
}
}Treap;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
int n;
signed main()
{
srand(time(NULL));
n = read();
for(re int i=1; i<=n; i++)
{ Treap.insert(read(),i); printf("%d\n",Treap.bst[root].len); }
return 0;
}
}
signed main()
{ return OMA::main(); }
最后:
建议尝试一下e
不难,主席树很好写,但也可以用平衡树来写。可以尝试一下。