「学习笔记」bzoj 3224 & bzoj 3223 平衡树两件套 - 非旋treap - 学习笔记

本文详细介绍了非选Treap的数据结构特点及其在解决特定问题中的应用,包括实现细节和注意事项,通过bzoj3224和bzoj3223两个题目展示了非选Treap的有效性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

非选treap真是个好东西!
bzoj3224有个实现细节我看网上都是错的,即split的时候,可能有重复元素(也就是1 x了相同的x很多次),然后求kth的时候不能把那个点拆成两个(否则merge的时候正确性和代码复杂度和时间复杂度都不能保证……吧?)。还有就是非旋treap其实不需要像splay一样一开始插入一个inf和INF来减少特判。
bzoj 3224:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define ull unsigned long long
#define gc getchar()
#define mp make_pair
#define fir first
#define sec second
#define N 100010
using namespace std;
typedef pair<int,int> pii;
inline int inn()
{
    int x=0,s=1,ch;while(((ch=gc)<'0'||ch>'9')&&ch!='-');
    if(ch^'-') x=ch^'0';else s=-1;
    while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');
    return s*x;
}
int ch[N][2],sz[N],cnt[N],val[N];ull key[N];
struct Rand{
    ull x;Rand() { x=0; }
    inline ull operator()() { return x*=998244353,x^=1000000007,x^=(x<<13)^(x>>5)^945632; }
}rnd;
struct Treap{
    int rt,node_cnt;
    Treap() { rt=node_cnt=0,insert(INT_MIN+10),insert(INT_MAX-10),cnt[1]=cnt[2]=sz[1]=sz[2]=0; }
    inline int push_up(int x) { return sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x]; }
    inline int setc(int x,int y,int z) { return ch[x][z]=y,push_up(x); }
    inline int new_node(int v,int x=0) { return x=++node_cnt,val[x]=v,key[x]=rnd(),cnt[x]=sz[x]=1,x; }
    int merge(int x,int y)
    {
        if(!x||!y) return x+y;
        if(key[x]<key[y]) return setc(x,merge(ch[x][1],y),1),x;
        else return setc(y,merge(x,ch[y][0]),0),y;return 0;
    }
    pii splitk(int x,int k)
    {
        if(!x||!k) return mp(0,x);pii t;
        if(k<=sz[ch[x][0]])
            return t=splitk(ch[x][0],k),setc(x,t.sec,0),mp(t.fir,x);
        else return k-=sz[ch[x][0]]+cnt[x],k=max(k,0),
            t=splitk(ch[x][1],k),setc(x,t.fir,1),mp(x,t.sec);
        return mp(0,0);
    }
    pii splitv(int x,int v)
    {
        if(!x) return mp(0,0);pii t;
        if(val[x]<=v) return t=splitv(ch[x][1],v),setc(x,t.fir,1),mp(x,t.sec);
        else return t=splitv(ch[x][0],v),setc(x,t.sec,0),mp(t.fir,x);
        return mp(0,0);
    }
    inline int insert(int v)
    {
        pii a=splitv(rt,v),b=splitv(a.fir,v-1);
        if(b.sec) cnt[b.sec]++,sz[b.sec]++;
        else b.sec=new_node(v);
        return rt=merge(merge(b.fir,b.sec),a.sec);
    }
    inline int del(int v)
    {
        pii a=splitv(rt,v),b=splitv(a.fir,v-1);
        if(!b.sec||!(--cnt[b.sec])) return rt=merge(b.fir,a.sec);
        return sz[b.sec]--,rt=merge(merge(b.fir,b.sec),a.sec);
    }
    inline int kth(int k)
    {
        pii t=splitk(rt,k);int x=t.fir;
        while(ch[x][1]) x=ch[x][1];
        return rt=merge(t.fir,t.sec),val[x];
    }
    inline int pre(int v)
    {
        pii t=splitv(rt,v-1);int x=t.fir;
        while(ch[x][1]) x=ch[x][1];
        return rt=merge(t.fir,t.sec),val[x];
    }
    inline int post(int v)
    {
        pii t=splitv(rt,v);int x=t.sec;
        while(ch[x][0]) x=ch[x][0];
        return rt=merge(t.fir,t.sec),val[x];
    }
    inline int rank(int v)
    {
        pii a=splitv(rt,v),b=splitv(a.fir,v-1);
        int ans=sz[b.fir]+(b.sec>0);
        return rt=merge(merge(b.fir,b.sec),a.sec),ans;
    }
}s;
int main()
{
    for(int n=inn();n;n--) switch(inn())
    {
        case 1:s.insert(inn());break;
        case 2:s.del(inn());break;
        case 3:printf("%d\n",s.rank(inn()));break;
        case 4:printf("%d\n",s.kth(inn()));break;
        case 5:printf("%d\n",s.pre(inn()));break;
        case 6:printf("%d\n",s.post(inn()));break;
        default:printf("TAT\n");
    }
    return 0;
}

bzoj 3223

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<utility>
#include<climits>
#define gc getchar()
#define ull unsigned long long
#define N 100010
#define mp make_pair
#define fir first
#define sec second
using namespace std;
typedef pair<int,int> pii;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int rt,node_cnt,ch[N][2],rev[N],val[N],key[N],sz[N],ans[N],c;
struct Rand{
    ull x;Rand(){ x=0; }
    inline ull operator()() { return (x*=998244353)^=1000000007; }
}rnd;
inline int new_node(int v,int x=0) { return x=++node_cnt,val[x]=v,key[x]=rnd(),sz[x]=1,x; }
inline int push_up(int x) { return sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; }
inline int setc(int x,int y,int z) { return ch[x][z]=y,push_up(x); }
inline int push_down(int x)
{
    if(!rev[x]) return 0;
    if(ch[x][0]) rev[ch[x][0]]^=1;
    if(ch[x][1]) rev[ch[x][1]]^=1;
    return swap(ch[x][0],ch[x][1]),rev[x]=0;
}
int merge(int x,int y)
{
    if(!x||!y) return x+y;push_down(x),push_down(y);
    if(key[x]<key[y]) return setc(x,merge(ch[x][1],y),1),x;
    else return setc(y,merge(x,ch[y][0]),0),y;return 0;
}
pii split(int x,int k)
{
    if(!x||!k) return mp(0,x);pii t;push_down(x);
    if(k<=sz[ch[x][0]]) return t=split(ch[x][0],k),setc(x,t.sec,0),mp(t.fir,x);
    else return t=split(ch[x][1],k-sz[ch[x][0]]-1),setc(x,t.fir,1),mp(x,t.sec);
}
inline int reverse(int l,int r)
{
    pii a=split(rt,r),b=split(a.fir,l-1);
    return rev[b.sec]^=1,rt=merge(merge(b.fir,b.sec),a.sec);
}
int build(int a,int b)
{
    if(a>b) return 0;int c=(a+b)>>1,x=new_node(c);
    return setc(x,build(a,c-1),0),setc(x,build(c+1,b),1),x;
}
int getans(int x) { return x?(push_down(x),getans(ch[x][0]),ans[++c]=val[x],getans(ch[x][1])):0; }
int main()
{
    int n=inn(),m=inn();rt=build(1,n);
    for(int l,r;m;m--) l=inn(),r=inn(),reverse(l,r);
    getans(rt);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return !printf("\n");
}
以下是一份使用Treap实现的C++代码,仅供参考。 ```c++ #include &lt;bits/stdc++.h&gt; using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 1e5 + 5; int n, m; int root, cnt; struct Node { int val, key; int size; int ch[2]; } tr[MAXN]; inline int new_node(int val) { tr[++cnt].val = val; tr[cnt].key = rand(); tr[cnt].size = 1; return cnt; } inline void pushup(int p) { tr[p].size = 1 + tr[tr[p].ch[0]].size + tr[tr[p].ch[1]].size; } inline void split(int p, int k, int &amp;x, int &amp;y) { if (!p) { x = y = 0; return; } if (tr[p].val &lt;= k) { x = p; split(tr[p].ch[1], k, tr[p].ch[1], y); } else { y = p; split(tr[p].ch[0], k, x, tr[p].ch[0]); } pushup(p); } inline int merge(int x, int y) { if (!x || !y) return x + y; if (tr[x].key &lt; tr[y].key) { tr[x].ch[1] = merge(tr[x].ch[1], y); pushup(x); return x; } else { tr[y].ch[0] = merge(x, tr[y].ch[0]); pushup(y); return y; } } inline void insert(int val) { int x, y; split(root, val, x, y); root = merge(merge(x, new_node(val)), y); } inline void remove(int val) { int x, y, z; split(root, val, x, z); split(x, val - 1, x, y); y = merge(tr[y].ch[0], tr[y].ch[1]); root = merge(merge(x, y), z); } inline int kth(int k) { int p = root; while (p) { if (tr[tr[p].ch[0]].size &gt;= k) { p = tr[p].ch[0]; } else if (tr[tr[p].ch[0]].size + 1 == k) { return tr[p].val; } else { k -= tr[tr[p].ch[0]].size + 1; p = tr[p].ch[1]; } } return -1; } inline int query_min() { int p = root; int res = INF; while (p) { res = min(res, tr[p].val); p = tr[p].ch[0]; } return res; } int main() { srand(time(NULL)); scanf(&quot;%d%d&quot;, &amp;n, &amp;m); for (int i = 1, opt, x; i &lt;= n; ++i) { scanf(&quot;%d%d&quot;, &amp;opt, &amp;x); if (opt == 1) { insert(x); } else { remove(tr[cnt].val); } } printf(&quot;%d\n&quot;, query_min()); while (m--) { int k; scanf(&quot;%d&quot;, &amp;k); printf(&quot;%d\n&quot;, kth(k)); } return 0; } ``` 代码实现中使用了一个结构体 `Node` 来表示平衡树的每一个节点。其中 `val` 表示节点存储的值,`key` 表示随机生成的优先级,`size` 表示以该节点为根的子树大小,`ch[0]` 和 `ch[1]` 分别表示左右儿子的下标。接下来就是一些经典的平衡树操作,例如 `split` 和 `merge` 用于分裂和合并平衡树,`insert` 和 `remove` 用于插入和删除节点,`kth` 和 `query_min` 用于查询平衡树中第 $k$ 小的数和最小值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值