二逼平衡树

题目链接:https://www.luogu.org/problemnew/show/P3380

这题解法真是多,各种数据结构的模板练习题啊。。(在此之前下面四个一个都不会)

  1. 查询k在区间内的排名

  2. 查询区间内排名为k的值

  3. 修改某一位值上的数值

  4. 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)

  5. 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)

1.线段树套平衡树

对于线段树的每一个节点,开一颗splay,维护序列中的元素

操作1,类似于线段树区间查询,查询在线段树的点的排名的和 nlognlogn

操作2,二分答案 nlognlognlogn 

由于每颗splay的形态不一定一样,所以他不能和线段树一样在树上二分

看了别人代码学习来的,直接对数的大小进行二分,满足条件的最大数一定在数列上(刚开始还想着在splay上二分 就复杂多了)

操作3. splay上插入删除 nlognlogn

操作4(5同理).每颗splay上查找找最大值

代码个人感觉如果splay和线段树模板打的熟是要比带修主席树好写的

 

#include <bits/stdc++.h>
using namespace std;
#define mid (h+t)/2
#define INF 1e9
#define INF2 2147483647
const int maxn=51111;
const int maxn2=2111111;
int a[maxn],count2[maxn2],leftson[maxn2],rightson[maxn2],
fa[maxn2],root[maxn*4],data[maxn2],num,n,m;
bool tt;
struct re
{
  int h,t;
}p[maxn*4];  
void updata(int x)
{
  count2[x]=count2[leftson[x]]+count2[rightson[x]]+1;
}
void rotate(int x,int y)
{
    int father=fa[x];
    if (y==1)
    {
        rightson[father]=leftson[x]; 
        if (leftson[x]) fa[leftson[x]]=father;
    } else
    {
        leftson[father]=rightson[x];
        if (rightson[x]) fa[rightson[x]]=father;
    }
    fa[x]=fa[father];
    if (fa[father])
    {
        if (leftson[fa[father]]==father)
          leftson[fa[father]]=x;
        else rightson[fa[father]]=x; 
    }
    fa[father]=x;
    if (y==1) leftson[x]=father; else rightson[x]=father;
    updata(father); updata(x);
}
void splay(int x,int goal,int z)
{
    if (x==root[z]) return;
    int father;
    while (fa[x]!=goal)
    {
        father=fa[x];
        if (fa[father]==goal)
        {
            if (x==leftson[father]) rotate(x,2);
            else rotate(x,1);
        } else
        {
            if (father==leftson[fa[father]])
            {
                if (x==leftson[father])
                  rotate(father,2),rotate(x,2);
                else rotate(x,1),rotate(x,2);
            } else
            {
                if (x==rightson[father])
                  rotate(father,1),rotate(x,1);
                else rotate(x,2),rotate(x,1);
            }
        }  
    }
    if (goal==0) root[z]=x;
}
void insert2(int x,int y,int z)
{
  while (x)
  {
    count2[x]++;
    if (y<data[x])
    {
      if (!leftson[x]) break;
      x=leftson[x];
    } else
    {
      if (!rightson[x]) break;
      x=rightson[x];
    }
  }
  data[++num]=y; fa[num]=x; count2[num]=1;
  if (x)
  { 
    if (y>=data[x]) rightson[x]=num; 
    else leftson[x]=num;
  }
  splay(num,0,z);
}
int query2(int x,int goal)
{
  int ans=0;
  while (x)
  {
    if (data[x]==goal) tt=1;
    if (data[x]<goal) ans+=count2[leftson[x]]+1,x=rightson[x]; else
    x=leftson[x];
  }
  return(ans);
}
int search(int x,int goal)
{
  while (x)
  {
    if (data[x]==goal) return(x);
    if (data[x]<goal) x=rightson[x];
    else x=leftson[x];
  }
}
void delete2(int number,int goal)
{
  splay(goal,0,number);
  int x=leftson[goal];
  if (x==0)
  {
    root[number]=rightson[goal];
    fa[root[number]]=0; return;
  }
  while (rightson[x]) x=rightson[x];
  splay(x,goal,number);
  rightson[x]=rightson[goal];
  if (rightson[goal]) fa[rightson[goal]]=x;
  updata(x); fa[x]=0; root[number]=x;
} 
void build(int x,int h,int t)
{
   p[x].h=h; p[x].t=t;
   for (int i=h;i<=t;i++) 
   insert2(root[x],a[i],x);
   if (h==t) return;
   build(x*2,h,mid); build(x*2+1,mid+1,t);
}
int query(int x,int h,int t,int goal)
{
  if (p[x].h>t||p[x].t<h) return(0);
  if (h<=p[x].h&&p[x].t<=t)
  {
    return(query2(root[x],goal));
  }
  return(query(x*2,h,t,goal)+query(x*2+1,h,t,goal));
}
void delete1(int x,int pos,int goal)
{
  int h=p[x].h,t=p[x].t;
  int y=search(root[x],goal);
  delete2(x,y); 
  if (h==t) return;
  if (pos<=mid) delete1(x*2,pos,goal); else delete1(x*2+1,pos,goal); 
}
void insert1(int x,int pos,int goal)
{
  int h=p[x].h,t=p[x].t;
  insert2(root[x],goal,x); 
  if (h==t) return;
  if (pos<=mid) insert1(x*2,pos,goal); else insert1(x*2+1,pos,goal);
}
int k_th(int x,int y,int k)
{
  int h=1,t=INF;
  while (h<t)
  {
    int midd=(h+t+1)/2; 
    if (1+query(1,x,y,midd)<=k) h=midd; else t=midd-1;
  }
  return(h);
}
int main()
{
  freopen("noip.in","r",stdin);
  freopen("noip.out","w",stdout);
  cin>>n>>m;
  for (int i=1;i<=n;i++) cin>>a[i];
  build(1,1,n);
  int c,d,e,f;
  for (int i=1;i<=m;i++)
  {
    cin>>c;
    if (c==3) cin>>d>>e; else cin>>d>>e>>f;
    if (c==1)
    {
      cout<<query(1,d,e,f)+1<<endl;
    }
    if (c==2)
    {
      cout<<k_th(d,e,f)<<endl;
    }
    if (c==3)
    {
      delete1(1,d,a[d]);
      insert1(1,d,e);
      a[d]=e;
    }
    if (c==4)
    {
      int x=query(1,d,e,f)+1; 
      if (x==1) cout<<-INF2<<endl; else
      {
        cout<<k_th(d,e,x-1)<<endl;  
      } 
    }
    if (c==5)
    {
      tt=0;
      int x=query(1,d,e,f)+1;
      if (x==e-d+2-tt) cout<<INF2<<endl; else
      {
        cout<<k_th(d,e,x+tt)<<endl;
      }
    }
  }
  return 0;
}

 

 

 

 

似乎得卡常 洛谷不开o2tle

 

2.cdq分治(并没有看懂以后再学吧,洛谷上有题解)

3.树状数组套主席树

第一维是树状数组,每个里面开一颗线段树(这个刚开始有点难理解,每个节点里维护的其实并不是任何东西的答案,是为了配合树状数组的使用)

前3个操作就是基本操作吧

对于4/5,计算出这个数的排名+-1再在树上二分就可以了

但是允许元素不出现在原序列就导致5的判断很恶心了

1.查找的时候如果出现在原数列那么是+1不然就是这个排名

2.还有一个坑 就是检验是不是最后一个数时

将这个数搞成离散化的数二分的时候 要注意它可能比最后一个数还要大 所以要特殊处理

*从这个中注意到要注意二分查找的边界限制,显然这个程序中的二分是找到比它小的值,如果这个值比它大那么就要处理了

*待修主席树的核心还是很好写的 然而这道题的细节就很不友善了

说一下这一题代码的实现,离散化是数据结构常有的,核心部分很好写,把查询x的排名变为查询比x小的数有多少个再+1(写平衡树写多了很容易忘记这个)

还有就是空间,(n+m)logn logn 事实我的代码开了100倍,讲道理最坏情况是要500倍左右

#include <bits/stdc++.h>
using namespace std;
#define maxn 100000
#define INF 2147483647
int root[maxn],b[maxn],d[maxn],e[maxn],rea[maxn],cnt,num,n,m,lll;
int tmp1[maxn],tmp2[maxn];
bool tt;
struct re
{
    int a,b,c,d;
}a[maxn],c[maxn],f[maxn];
struct ree
{
    int x,leftson,rightson;
}p[maxn*50];
bool cmp(re x,re y)
{
          if (x.a<y.a) return(true); else return(false);
}
void updata(int x)
{
    p[x].x=p[p[x].leftson].x+p[p[x].rightson].x;
}
#define mid (h+t)/2
void change(int &x,int h,int t,int sum,int l)
{
    if (!x)
    {
        x=++cnt;
    }
    p[x].x+=l;
    if (h==t) return;
    if (sum<=mid) change(p[x].leftson,h,mid,sum,l);
    else change(p[x].rightson,mid+1,t,sum,l);
    updata(x);
}
int query2(int x,int h,int t,int num)
{
    if (x==0||h>num) return(0);
    if (t<num) return(p[x].x);
    return(query2(p[x].leftson,h,mid,num)+query2(p[x].rightson,mid+1,t,num));
}
#define lowbit(x) (x&-x)
void insert(int x,int y,int l)
{
    while (x<=n)
    {
        change(root[x],1,lll,y,l);
        x+=lowbit(x);
    }
}
int query(int x,int y)
{
    int ans=0;
    while (x)
    {
        ans+=query2(root[x],1,lll,y);
        x-=lowbit(x);
    }
    return(ans);
}
struct ret
{
    int a,c; bool b;
};
int find(int x)
{
    int h=1,t=num;
    while (h<t)
    {
        if (f[mid].a<x) h=mid+1; else t=mid;
    }
    if (f[h].a==x) tt=1;
    if (f[h].a<x) return(f[h].d+1);
    else return(f[h].d);
}
int k_th(int x,int y,int z)
{
    int num1=0,num2=0; x--;
    while (x)
    {
        tmp1[++num1]=root[x]; x-=lowbit(x);
  } 
  while (y)
  {
      tmp2[++num2]=root[y]; y-=lowbit(y);
    }
    int h=1,t=lll,tmp;
  while (h!=t)
  {
        tmp=0;
        for (int i=1;i<=num1;i++) tmp-=p[p[tmp1[i]].leftson].x;
        for (int i=1;i<=num2;i++) tmp+=p[p[tmp2[i]].leftson].x;
        if (tmp>=z)
        {
        t=mid;
            for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].leftson;
            for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].leftson;
          } else
          {
              h=mid+1;z-=tmp;
              for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].rightson;
              for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].rightson;
          }
    }
    return (rea[h]);
}
bool check(int x,int y,int goal)
{
    int num1=0,num2=0; x--;
    while (x)
    {
        tmp1[++num1]=root[x]; x-=lowbit(x);
  } 
  while (y)
  {
      tmp2[++num2]=root[y]; y-=lowbit(y);
    }
    int h=1,t=lll,tmp=0;
    while (h!=t)
    {
        if (goal<=mid)
        {
             t=mid;
           for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].leftson;
         for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].leftson;
        } else
        {
             h=mid+1;
           for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].rightson;
           for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].rightson;
        }
    }
     for (int i=1;i<=num1;i++) tmp-=p[tmp1[i]].x;
   for (int i=1;i<=num2;i++) tmp+=p[tmp2[i]].x;
   if (tmp) return(1); else return(0);
}
int main()
{
      std::ios::sync_with_stdio(false); 
      freopen("noip.in","r",stdin);
      freopen("noip.out","w",stdout);
      cin>>n>>m;
      for (int i=1;i<=n;i++) cin>>a[i].a,f[i].a=a[i].a,f[i].b=i,f[i].c=0;
      num=n;
      for (int i=1;i<=m;i++)
      {
            cin>>b[i];
            if (b[i]==1) cin>>c[i].a>>d[i]>>e[i];
            if (b[i]==2) cin>>c[i].a>>d[i]>>e[i];
            if (b[i]==3)
            {
                cin>>c[i].a>>d[i];
                f[++num].a=d[i]; f[num].b=i;f[num].c=1;
                }
                if (b[i]==4) cin>>c[i].a>>d[i]>>e[i];
                if (b[i]==5) cin>>c[i].a>>d[i]>>e[i];
        }
        sort(f+1,f+num+1,cmp); f[0].a=-INF;
        for (int i=1;i<=num;i++)
        {
            if (f[i].a!=f[i-1].a) lll++; f[i].d=lll; rea[lll]=f[i].a;
            if (f[i].c==0) a[f[i].b].b=lll,a[f[i].b].c=i;
            else c[f[i].b].b=lll,c[f[i].b].c=i;
        }
        //for (int i=1;i<=num;i++) cout<<f[i].a<<endl;
        for (int i=1;i<=n;i++)
        {
            insert(i,a[i].b,1);
        }
        for (int i=1;i<=m;i++)
        {
            if (b[i]==1)
            {
                int x=find(e[i]);
                cout<<1+query(d[i],x)-query(c[i].a-1,x)<<endl;  //zhuyi0
            }
            if (b[i]==2)
            {
                cout<<k_th(c[i].a,d[i],e[i])<<endl; 
            }
            if (b[i]==3)
            {
          insert(c[i].a,a[c[i].a].b,-1);
          insert(c[i].a,c[i].b,1);
          a[c[i].a].b=c[i].b;
          } 
          if (b[i]==4)
          {
              int x=find(e[i]);
              int y=query(d[i],x)-query(c[i].a-1,x)+1;
              if (y==1) cout<<-INF<<endl;
          else cout<<k_th(c[i].a,d[i],y-1)<<endl;
            }
            if (b[i]==5)
            {
                tt=0;int x=find(e[i]);
                if(tt==1&&check(c[i].a,d[i],x)) tt=1; else tt=0;
              int y=query(d[i],x)-query(c[i].a-1,x)+1;
              if (y==d[i]-c[i].a+2-tt) cout<<INF<<endl;
          else cout<<k_th(c[i].a,d[i],y+tt)<<endl;
            }
        }
}

 

4.分块

转载于:https://www.cnblogs.com/yinwuxiao/p/8419947.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值