「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(); }

火星人prefix

每个节点代表子树的哈希值。按位置分裂,这样就可以查询区间的哈希值了。

对于区间哈希值,则有

\[hash(p)=hash(rs)+hash(ls)\times base^{size(rs)+1}+val(p)\times base^{size(rs)} \]

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
不难,主席树很好写,但也可以用平衡树来写。可以尝试一下。

posted @ 2021-07-07 18:04  -OMA-  阅读(154)  评论(3编辑  收藏  举报
浏览器标题切换
浏览器标题切换end