洛谷P3391 【模板】文艺平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列。

其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4] 的话,结果是5 2 3 4 1。

输入格式

第一行两个正整数n,m,表示序列长度与操作个数。序列中第i项初始为i。
接下来m行,每行两个正整数l,r,表示翻转的区间。

输出格式

输出一行n个正整数,表示原始序列经过m次变换后的结果。

输入输出样例

输入 #1
5 3
1 3
1 3
1 4

 

输出 #1
4 3 2 1 5

说明/提示

【数据范围】
对于100% 的数据,1n,m100000,1lrn。

思路:明显平衡树维护区间修改,接近于板子题

值得一说的是,fhq-treap在维护序列时,void split(int p,int x,int &l,int &r)的意思不再是分离<=k和>k的两棵树

而是变为了 将序列p的前x个元素分离出来形成一棵新fhq-treap的l子树,另外的一部分形成另一颗 fhq-treap的r 子树

所以,split你会发现写法像极了前面的普通平衡树的getnum函数

而且新增了懒标记(线段树诈尸!)用来标记区间是否被翻转了,

还有pushdown也是不同于普通平衡树,显然是懒标记使用的后果

仍reverse等,但由于这是这道题中的写法,别的题中不一定适用,此处不多赘述

复制代码
#include<bits/stdc++.h>
using namespace std;
struct node{
    int l,r,key,val,si;
    bool flag;
}tr[200010];
int n,m,idx,dl,dr,tmp,rt;
int getrand(int x){
    tr[++idx].key = x;
    tr[idx].val = rand();
    tr[idx].si = 1;
    return idx;
}
void pushup(int p){
    tr[p].si = tr[tr[p].l].si+tr[tr[p].r].si+1;
}
void pushdown(int p){
    if(tr[p].flag){
        tr[tr[p].l].flag^=1;//用异或是因为翻转两次即可不用翻转 
        tr[tr[p].r].flag^=1;
        swap(tr[p].l,tr[p].r);
        tr[p].flag = 0;
    }
}
void split(int p,int x,int &l,int &r){
    if(!p){
        l = r = 0;
        return ;
    }
    pushdown(p);
    int u = tr[tr[p].l].si+1;
    if(u<=x){
        l = p;
        split(tr[l].r,x-u,tr[l].r,r);
        pushup(l);
    }else{
        r = p;
        split(tr[r].l,x,l,tr[r].l);
        pushup(r);
    }
}
int merge(int l,int r){
    if(!l||!r)
        return l|r;
    pushdown(l);
    pushdown(r);
    if(tr[l].val<=tr[r].val){
        tr[l].r = merge(tr[l].r,r);
        pushup(l);
        return l;
    }else{
        tr[r].l = merge(l,tr[r].l);
        pushup(r);
        return r;
    }
}void reverse(int l,int r){
    int p1 = 0,p2 = 0,p3 = 0,p4 = 0;
    split(rt,l-1,p1,p2);
    split(p2,r-l+1,p3,p4);
    tr[p3].flag^=1;
    p2 = merge(p3,p4);
    rt = merge(p1,p2);
}
void printres(int p){
    pushdown(p);
    if(tr[p].l) printres(tr[p].l);
    printf("%d ",tr[p].key);
    if(tr[p].r) printres(tr[p].r);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1;i<=n;i++){
        split(rt,i-1,dl,dr);
        rt = merge(merge(dl,getrand(i)),dr);
    }
    int x,y;
    while(m--){
        scanf("%d%d",&x,&y);
        reverse(x,y);
    }
    printres(rt);
    return 0;
}
复制代码

综上所述,我是蒟蒻

2022-10-28 17:46:37

posted @   cztq  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示