洛谷P3391【无旋treap】

什么叫tnd的优雅?这tnd的就是优雅!

普通的treap如果遇到区间序列问题就没办法了

比如这道splay的模板题,根本没法做。

但是总会有神仙让他可以做!

我们现在不需要insert和del了,因为都是针对单点维护的操作。

我们要学会区间insert和区间del!

split(int rt, int k, int &x, int &y)
将一整棵树分成两棵树,k就是划分的依据,我们可以根据需求将权值小于k的划分到一棵树,权值
大于k的划分到另外一棵树。也可以把前k个划分到一棵树,剩下的划分到另外一棵树。

具体的操作就是根据二叉排序树的特性,假设要分成x,y两棵树,约定x为的权值或序列小的一边。

如果当前节点的权值大于k(按权值划分)或者左子树的节点数大于k(按序列划分)那么当前节点肯定是属于y树的反之就是x树。这个时候我们只需要考虑x与y的左子树的关系了(结合二叉树的特性想一想为什么),反之只需要考虑x的右子树和y的关系。

merge(int A, int B)
将A,B两棵树合并成一棵树。
结合的时候要考虑到二叉树的左>中>右的特性,的话我们只有两种可能的合并方式,但是别忘了treap是有随机权的,这样可以让我们的树更一点。所以我们可以让权重大的在上。。。
如果你没听懂,推荐一个我看的博客。
可能有配图要容易理解的多。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
struct Random {
  int sed, A, C, M;
  Random(int sed = rand(), int A = 48271, int C = 57, int M = 2147483647)
      : sed(sed), A(A), C(C), M(M) {}
  int out() { return (sed = ((A * sed + C) % M)); }
} A;
struct Treap{
    int ch[maxn][2], val[maxn];
    int dat[maxn], size[maxn], flag[maxn];
    int root, tot;
    int newnode(int v) {
        val[++tot] = v;
        dat[tot] = A.out();
        size[tot] = 1;
        return tot;
    }
    void pushup(int rt) {
        size[rt] = 1 + size[ch[rt][0]] + size[ch[rt][1]];
    }
    void pushdown(int rt) {
        if(flag[rt]) {
            swap(ch[rt][0], ch[rt][1]);
            if(ch[rt][0]) flag[ch[rt][0]] ^= 1;
            if(ch[rt][1]) flag[ch[rt][1]] ^= 1;
            flag[rt] = 0;
        }
    }
    void split(int rt, int k, int &x, int &y) {
        if(!rt) x = y = 0;
        else {
            pushdown(rt);
            if(k <= size[ch[rt][0]]) {
                y = rt;
                split(ch[rt][0], k, x, ch[rt][0]);
            }
            else {
                x = rt;
                split(ch[rt][1], k-size[ch[rt][0]]-1, ch[rt][1], y);
            }
            pushup(rt);
        }
    }
    int merge(int A, int B) {
        if(!A || !B) return A + B;
        if(dat[A] < dat[B]) {
            pushdown(A);
            ch[A][1] = merge(ch[A][1], B);
            pushup(A);
            return A;
        }
        else {
            pushdown(B);
            ch[B][0] = merge(A, ch[B][0]);
            pushup(B);
            return B;
        }
    }
    void dfs(int rt) {
        if(!rt) return;
        pushdown(rt);
        dfs(ch[rt][0]);
        printf("%d ", val[rt]);
        dfs(ch[rt][1]);
    }
}treap;
void solve() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        treap.root = treap.merge(treap.root, treap.newnode(i));
    }
    for(int i = 1; i <= m; i++) {
        int l, r, a, b, c;
        scanf("%d%d", &l, &r);
        treap.split(treap.root, l-1, a, b);
        treap.split(b, r-l+1, b, c);
        treap.flag[b] ^= 1;
        treap.root = treap.merge(a, treap.merge(b, c));
    }
    treap.dfs(treap.root);
    return;
}
int main() {
	srand(time(0));
    //g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt","w",stdout);
#endif
while(Case--) {
    solve();
    }
return 0;
}
全部评论

相关推荐

10-19 10:28
已编辑
成都理工大学 后端工程师
团孝子已上线feeling:面了很多家公司,能感受到目前只有小公司+外包喜欢问八股。大厂虽然也问八股,但是是从实习、项目中进行提问,并且大厂会问很深,面试官也会对你的回答进行思考➕追问,所以准备大厂面试前一定要备好相关资料。对于算法,我做的是codetop前100+力扣hot100+力扣高频150,面试中实感hot100就足够,基本上只要是hot100就秒答。对于项目和八股,我做的也是烂大街的星球项目,八股则是看小林和问ai,自己也写了很多技术博客和画了很多思维导图,并且自己也尝试用嘴巴说出来,不只停留于纸面。运气也很重要,必须要让面试官/HR看到简历才行,所以建议投递时间是下午两点。tl:第一岗位9.9&nbsp;投递9.10&nbsp;一面(一面评价:最近见过最强的大三,结束五分钟后约二面,都晚上九点了不下班吗)9.11&nbsp;二面(三道算法a出两道,反问评价:经验不够等横向,我实习生要啥经验)9.21挂(实习时间过短+其他原因,想要一年实习的,为什么不招个正职)第二岗位10.10投递10.11约面(主管打电话,说看到我之前投递记录了想要我挂qa职进去干后端,同意)10.14&nbsp;一面(无八股,主动说确实很强,意愿很强)10.16&nbsp;oc其余,友邦,东软,东华,惠择,用友oc已拒京东测开一面挂(投后端被测开捞)腾讯测试已拒(投后端被测开捞)ps:表扬惠择的主管面,没怎么问技术(可能是一面面试官沟通过了),全程一起讲大道理,解答了心中很多疑惑,也告诉我以面试官角度来看怎么选候选人,如果可以下次一定选惠择
HeaoDng:美团好像可以触发一面通
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务