[洛谷P3380] 二逼平衡树(树套树)

https://www.luogu.org/problemnew/show/P3380

这题我是用线段树+splay写的,第一次写树套树,一开始感觉是个很难想象很难写的事情,但说白了这一题就是线段树的每个区间套一个splay。。。

然后对于第二个操作就是二分答案val,验证rank值。。。

第一个版本写完无限TLE...

改了一个晚上终于改到了洛谷5000多ms,bzoj7200ms。。。写此文只是纪念一下我第一个树套树。。。

结合别人代码后有几点小优化:

1、发现对于第二个二分查询,找前继后缀的时候,不能每次都splay,单独写一个后,bzoj上快了1300ms

2、还是对于第二个操作,可以开一个vector存下每个需要查询的区间段的rt,二分时直接for就行,不用每次都查询哪几个区间,bzoj上快了600ms

3、对于delete元素的操作,采用只查找前继pre,然后此时需要delete的数x在root上,若cnt>1就直接减一个退出,若否,则splay前继pre到root,oldrt为x对应的下标,即上一个根节点,然后ch[pre][1]=ch[oldrt][1];par[ch[oldrt][1]]=pre;因为把前继splay,不会影响到oldrt的右半部分的节点,而且那时的前继也肯定是没有右儿子的,不然就不是前继了,所以当前继splay上来后,只需上面的操作即可。也可以快一点。。。

4、刚开始插入完毕后应该是一条链,如果这个时候开始第二个操作就会变成O(n)的了,可以先预处理splay一下,但是不好确定哪些点是该rt的(未实现想法)。。。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define FI first
#define SE second
#define MP make_pair
#define PI pair<int,int>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define test printf("here!!!\n")
using namespace std;
const int mx=5e4+10;
const int MAX=4e6+10;
int n,m;
int nl,nr,pos;
vector<int>vec;
struct Node
{
    int l,r,root;
}t[mx<<2|2];
int ch[MAX][2],cnt[MAX],sz[MAX],val[MAX],par[MAX],tot;
int a[mx];
int res;
int get(int x)
{
    return ch[par[x]][1]==x;
}
void pushup(int x)
{
    sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
}
void rot(int x)
{
    int y=par[x],z=par[y],k=get(x),w=get(y);
    ch[z][w]=x;
    par[x]=z;
    ch[y][k]=ch[x][k^1];
    par[ch[x][k^1]]=y;
    ch[x][k^1]=y;
    par[y]=x;
    pushup(y),pushup(x);
}
void splay(int rt,int x,int goal)
{
    while (par[x]!=goal)
    {
        int y=par[x],z=par[y];
        if (z!=goal)
        {
            get(x)==get(y)?rot(y):rot(x);
        }
        rot(x);
    }
    if (goal==0) t[rt].root=x;
}
void ins(int rt,int x)
{
    int r=t[rt].root,fa=0;
    while (val[r]!=x&&r)
    {
        fa=r;
        r=ch[r][x>val[r]];
    }
    if (r) ++cnt[r];
    else
    {
        r=++tot;
        if (fa) ch[fa][x>val[fa]]=r;
        ch[r][0]=ch[r][1]=0;
        par[r]=fa,val[r]=x;
        sz[r]=cnt[r]=1;
    }
    splay(rt,r,0);
}
int rrank(int rt,int x)
{
    int r=t[rt].root,hgf=0;
    while (r)
    {
        if (val[r]==x) return hgf+(ch[r][0]?sz[ch[r][0]]:0);
        else if (val[r]<x)
        {
            hgf+=(ch[r][0]?sz[ch[r][0]]:0)+cnt[r];
            r=ch[r][1];
        }
        else r=ch[r][0];
    }
    return hgf;
}
int rrfind(int rt,int x)
{
    int r=t[rt].root,hgf=0;
    while (r)
    {
        if (val[r]==x) return hgf+(ch[r][0]?sz[ch[r][0]]:0)+cnt[r];
        else if (val[r]<x)
        {
            hgf+=(ch[r][0]?sz[ch[r][0]]:0)+cnt[r];
            r=ch[r][1];
        }
        else r=ch[r][0];
    }
    return hgf;
}
void findxb(int rt,int x)
{
    if (!t[rt].root) return;
    int r=t[rt].root;
    while (val[r]!=x&&ch[r][x>val[r]])
    {
        r=ch[r][x>val[r]];
    }
    splay(rt,r,0);
}
int presucc(int rt,int x,int k)
{
    findxb(rt,x);
    int r=t[rt].root;
    if ((val[r]<x&&!k)||(x<val[r]&&k)) return r;
    r=ch[r][k];
    int fk=k^1;
    while (ch[r][fk]) r=ch[r][fk];
    return r;
}
int splaypre(int rt,int x)
{
    int ans=-2147483647;
    int r=t[rt].root;
    while (r)
    {
        if (val[r]<x)
        {
            if (ans<val[r]) ans=val[r];
            r=ch[r][1];
        }
        else r=ch[r][0];
    }
    return ans;
}
int splaysucc(int rt,int x)
{
    int ans=2147483647;
    int r=t[rt].root;
    while (r)
    {
        if (val[r]>x)
        {
            if (ans>val[r]) ans=val[r];
            r=ch[r][0];
        }
        else r=ch[r][1];
    }
    return ans;
}
void del(int rt,int x)
{
    int pre=presucc(rt,x,0);
    int r=t[rt].root;
    if (val[r]==x)
    {
        if (cnt[r]>1)
        {
            --cnt[r];
            pushup(r);
            return;
        }
        int oldrt=t[rt].root;
        splay(rt,pre,0);
        ch[pre][1]=ch[oldrt][1];par[ch[oldrt][1]]=pre;
        pushup(pre);
    }
}
void build(int rt,int l,int r)
{
    ins(rt,-2147483647);
    for (int i=l;i<=r;++i)
    {
        ins(rt,a[i]);
    }
    ins(rt,2147483647);
    t[rt].l=l,t[rt].r=r;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
}
void segvec(int rt)
{
    if (nl<=t[rt].l&&t[rt].r<=nr)
    {
        vec.pb(rt);
        return;
    }
    int mid=(t[rt].l+t[rt].r)>>1;
    if (nl<=mid) segvec(rt<<1);
    if (nr>mid) segvec(rt<<1|1);
}
int segrfind(int rt,int c)
{
    if (nl<=t[rt].l&&t[rt].r<=nr)
    {
        return rrank(rt,c)-1;
    }
    int mid=(t[rt].l+t[rt].r)>>1;
    int ans=0;
    if (nl<=mid) ans+=segrfind(rt<<1,c);
    if (nr>mid) ans+=segrfind(rt<<1|1,c);
    return ans;
}
int segkth(int rt,int k)
{
    segvec(1);
    int ls=0;
    int rs=1e8;
    while (ls<=rs)
    {
        int mid=(ls+rs)>>1;
        int rk=0;
        for (int i=vec.size()-1;i>=0;--i)
        {
            rk+=rrfind(vec[i],mid)-1;
        }
        if (rk<k)
        {
            ls=mid+1;
        }
        else
        {
            rs=mid-1;
        }
    }
    return ls;
}
void segupdata(int rt,int c)
{
    del(rt,a[pos]),ins(rt,c);
    if (t[rt].l==t[rt].r) return;
    int mid=(t[rt].l+t[rt].r)>>1;
    if (pos<=mid) segupdata(rt<<1,c);
    else segupdata(rt<<1|1,c);
}
void segpresucc(int rt,int c,int k)
{
    if (nl<=t[rt].l&&t[rt].r<=nr)
    {
        if (!k) res=max(res,splaypre(rt,c));
        else res=min(res,splaysucc(rt,c));
        return;
    }
    int mid=(t[rt].l+t[rt].r)>>1;
    if (nl<=mid) segpresucc(rt<<1,c,k);
    if (nr>mid) segpresucc(rt<<1|1,c,k);
}
int main()
{
    int op,k;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
    }
    build(1,1,n);
    for (int i=1;i<=m;++i)
    {
        scanf("%d",&op);
        if (op!=3) scanf("%d%d%d",&nl,&nr,&k);
        else scanf("%d%d",&pos,&k);
        if (op==1)
        {
            printf("%d\n",segrfind(1,k)+1);
        }
        else if (op==2)
        {
            vec.clear();
            printf("%d\n",segkth(1,k));
        }
        else if (op==3)
        {
            segupdata(1,k);
            a[pos]=k;
        }
        else if (op==4)
        {
            res=-2147483647;
            segpresucc(1,k,0);
            printf("%d\n",res);
        }
        else
        {
            res=2147483647;
            segpresucc(1,k,1);
            printf("%d\n",res);
        }
    }
}

298行代码(虽然括号占了很多行2333),差一点超300了。。。

然后我用vector独立给每个区间段开点后终于到了346行,也完美的TLE了。。。

想想应该也会TLE的,平衡树本来下标之间就很跳跃,在整个大数组里跳跃和在vector里跳跃应该没什么大区别。。。

(所以我觉得图论里的vector存边比前式链向星存边快一点。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值