[BZOJ3600]没有人的算术 重量平衡树+线段树

先讲讲重量平衡树是什么,可以先看2013年国家集训队clj的论文。
重量平衡树首先要保证平衡而且不能是均摊平衡,然后要么没有旋转,要么旋转是影响的子树大小要期望 log log 或者均摊 log log 。Treap和替罪羊树都满足重量平衡树的条件。
那么对于这题,发现复杂度瓶颈在于比较两个数的大小(复杂度可能达到指数级),因为操作中出现的数的种类是 O(n) O ( n ) ,我们希望能给每个数分配一个实数值来 O(1) O ( 1 ) 比较大小,这样就变成线段树裸题了。
考虑把数放在平衡树上,对于每棵子树,根节点分配的值为 mid m i d ,左子树为 [l,mid) [ l , m i d ) ,右子树为 (mid,r] ( m i d , r ] 就可以了。那么首先要保证树不能太深(平衡),要不然精度不够,还有改变树的形态会导致子树中所有值重新计算,恰好符合重量平衡树的条件。
那么具体就是记 posi p o s i 表示第 i i <script type="math/tex" id="MathJax-Element-655">i</script>个数分配的值在平衡树上的位置,对于一个新合成的数,扔到平衡树上比较,如果没出现过就新建一个点,然后就是个线段树区间最大值了。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define ll long long
#define N 100010
using namespace std;
const double alpha=0.75;
const ll U=(1ll<<62)-1;
int n,m,top,tim;
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct sctree;
struct node
{
    sctree *l,*r;
};
sctree *NUL;
struct sctree
{
    int sz,id;
    node t;
    ll lv,rv,mv;
    sctree *ls,*rs;
    sctree(){sz=0;lv=rv=0;ls=rs=NUL;}
    sctree(node x,ll lx,ll rx)
    {
        t=x;sz=1;
        lv=lx;rv=rx;mv=(lv+rv)>>1;
        ls=rs=NUL;
    }
    void update()
    {
        sz=ls->sz+rs->sz+1;
    }
    bool isbad()
    {
        return (ls->sz>sz*alpha+5)||(rs->sz>sz*alpha+5);
    }
}*sctr,*zero,*pos[N],*st[500010];
bool operator <(node p,node q)
    {
        if(p.l->mv==q.l->mv) return p.r->mv<q.r->mv;
        return p.l->mv<q.l->mv; 
    }
bool operator ==(node p,node q)
    {
        return (p.l==q.l&&p.r==q.r);
    }
void travel(sctree *p)
{
    if(p==NUL) return ;
    travel(p->ls);
    st[++top]=p;
    travel(p->rs);
}
sctree *divide(int l,int r,ll lx,ll rx)
{
    if(l>r) return NUL;
    int mid=(l+r)>>1;
    sctree *p=st[mid];p->lv=lx;p->rv=rx;p->mv=(lx+rx)>>1;
    p->ls=divide(l,mid-1,lx,p->mv);
    p->rs=divide(mid+1,r,p->mv,rx);
    p->update();
    return p;
}
void rebuild(sctree *&p)
{
    top=0;
    travel(p);
    p=divide(1,top,p->lv,p->rv);
}
sctree ** insert(sctree *&p,node v,ll lx,ll rx,int k)
{
    if(p==NUL) 
    {
        p=new sctree(v,lx,rx);
        pos[k]=p;
        return &NUL;
    }
    sctree **re;
    p->sz++;
    if(v<p->t) re=insert(p->ls,v,p->lv,p->mv,k);
    else re=insert(p->rs,v,p->mv,p->rv,k);
    if(p->isbad())re=&p;
    return re;  
}
void ins(node v,int k)
{
    int re;
    sctree ** p=insert(sctr,v,0,U,k);   
    if(*p!=NUL) rebuild(*p);    
}

sctree *find(sctree *p,node v)
{
    if(p==NUL||v==p->t) return p;
    if(v<p->t) return find(p->ls,v);
    else return find(p->rs,v);
}
struct segtree
{
    int mx;
    segtree *ls,*rs;
    segtree(){ls=rs=0;}
    void update()
    {
        if(pos[ls->mx]->mv>=pos[rs->mx]->mv) mx=ls->mx;
        else mx=rs->mx;
    }
    void build(int l,int r)
    {
        if(l==r) {mx=l;return ;}
        int mid=(l+r)>>1;
        (ls=new segtree)->build(l,mid);
        (rs=new segtree)->build(mid+1,r);
        update();
    }
    void mdf(int l,int r,int x)
    {
        if(l==r) return ;
        int mid=(l+r)>>1;
        if(x<=mid) ls->mdf(l,mid,x);
        else rs->mdf(mid+1,r,x);
        update();
    }
    int qry(int l,int r,int lx,int rx)
    {
        if(l==lx&&r==rx) return mx;
        int mid=(l+r>>1);
        if(rx<=mid) return ls->qry(l,mid,lx,rx);
        else if(lx>mid) return rs->qry(mid+1,r,lx,rx);
        else
        {
            int lp=ls->qry(l,mid,lx,mid),rp=rs->qry(mid+1,r,mid+1,rx);
            if(pos[lp]->mv>=pos[rp]->mv) return lp;
            else return rp;
        }
    }
}*xtr;
int main()
{
    n=read();m=read();
    NUL=new sctree;NUL->ls=NUL->rs=NUL;sctr=NUL;
    zero=new sctree;zero->mv=-1;
    for(int i=1;i<=n;i++)
        pos[i]=zero;
    (xtr=new segtree)->build(1,n);   
    while(m--)
    {
        char opt;scanf("%s",&opt);
        int l=read(),r=read(),k;
        if(opt=='C')
        {
            k=read();
            node v=(node){pos[l],pos[r]};
            sctree *f=find(sctr,v);
            if(f!=NUL) pos[k]=f;
            else ins(v,k);
            xtr->mdf(1,n,k);
        }
        else printf("%d\n",xtr->qry(1,n,l,r));   
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值