P3391 文艺平衡树

hh

题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

输入输出格式

输入格式:

第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数

接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n

输出格式:

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

输入输出样例

输入样例#1:

5 3
1 3
1 3
1 4

输出样例#1:

4 3 2 1 5

说明

N,M<=100000

分析

不要问我为什么,我是抄的别人的代码

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<cstdlib>
  7 #include<ctime>
  8 using namespace std;
  9 const int MAXN=300001;
 10 static void read(int &n)
 11 {
 12     char c=‘+‘;int x=0;bool flag=0;
 13     while(c<‘0‘||c>‘9‘){c=getchar();if(c==‘-‘)flag=1;}
 14     while(c>=‘0‘&&c<=‘9‘){x=(x<<1)+(x<<3)+(c-48);c=getchar();}
 15     flag==1?n=-x:n=x;
 16 }
 17 int ch[MAXN][3];// 0左孩子 1右孩子
 18 int val[MAXN];// 每一个点的权值
 19 int pri[MAXN];// 随机生成的附件权值
 20 int siz[MAXN];// 以i为节点的树的节点数量
 21 int sz;// 总结点的数量
 22 int n,m;
 23 int x,y,z,a,b,root;
 24 int mark[MAXN];
 25 void update(int x)
 26 {
 27     siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];
 28 }
 29 void pushdown(int x)
 30 {
 31     if(x&&mark[x])
 32     {
 33         mark[x]=0;
 34         swap(ch[x][0],ch[x][1]);
 35         if(ch[x][0])    mark[ch[x][0]]^=1;
 36         if(ch[x][1])    mark[ch[x][1]]^=1;
 37     }
 38 }
 39 int new_node(int v)
 40 {
 41     siz[++sz]=1;// 新开辟一个节点
 42     val[sz]=v;
 43     pri[sz]=rand();
 44     return sz;
 45 }
 46
 47 int merge(int x,int y)// 合并
 48 {
 49     if(!x||!y)    return x+y;// x和y中必定有一个是0
 50     pushdown(x);pushdown(y);
 51     if(pri[x]<pri[y])// 把x加到左边的树上
 52     {
 53         ch[x][1]=merge(ch[x][1],y);// 不懂的看GIF图
 54         update(x);
 55         return x;
 56     }
 57     else
 58     {
 59         ch[y][0]=merge(x,ch[y][0]);
 60         update(y);
 61         return y;
 62     }
 63 }
 64 void split(int now,int k,int &x,int &y)
 65 {
 66     if(!now) x=y=0;// 到达叶子节点
 67     else
 68     {
 69         pushdown(now);
 70         if (k<=siz[ch[now][0]])
 71             y=now,split(ch[now][0],k,x,ch[now][0]);
 72         else
 73             x=now,split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y);
 74         update(now);
 75     }
 76 }
 77 int build(int l,int r)
 78 {
 79     if(l>r)    return 0;
 80     int mid=(l+r)>>1;int v=mid-1;
 81     int now=new_node(v);
 82     ch[now][0]=build(l,mid-1);
 83     ch[now][1]=build(mid+1,r);
 84     update(now);
 85     //pushdown(now);
 86     return now;
 87 }
 88 void dfs(int x)
 89 {
 90     if(!x)    return ;
 91     pushdown(x);
 92     dfs(ch[x][0]);
 93     if(val[x]>=1&&val[x]<=n)
 94         printf("%d ",val[x]);
 95     dfs(ch[x][1]);
 96 }
 97 void res(int l,int r)
 98 {
 99     int a,b,c,d;
100     split(root,r+1,a,b);
101     split(a,l,c,d);
102     mark[d]^=1;
103     root=merge(merge(c,d),b);
104 }
105 int main()
106 {
107     srand((unsigned)time(NULL));
108
109     read(n);read(m);
110     root=build(1,n+2);
111     for(int i=1;i<=m;i++)
112     {int l,r;read(l);read(r);res(l,r);}
113     dfs(root);
114     return 0;
115 }
时间: 2024-10-07 05:05:11

P3391 文艺平衡树的相关文章

luogu P3391 文艺平衡树

二次联通门 : luogu P3391 文艺平衡树 /* luogu 3391 文艺平衡树 splay 区间翻转 每次翻转区间[l,r]都是把l旋到根节点上, r旋到根节点的右节点上 那么要修改的区间就是根节点的右孩子的左子树 然后打标记.. 之后每次查到时就下放标记 原区间为1 ~ N 增加两个哨兵节点 0 和 N + 1, 避免翻转1~N的区间产生麻烦 */ #include <cstdio> #define Max 200009 inline int swap (int &a,

[Splay]luogu P3391 文艺平衡树

题目描述 https://www.luogu.org/problemnew/show/P3391 分析 文艺平衡树 一道大家熟知的splay区间翻转模板题 基于splay的区间翻转,我们要做的只有这些: 1.像线段树一样打翻转标记,不过由于翻转是可以抵消的,所以可以采取位运算节省时间 2.翻转只需要逐层翻转即可,正确性已有论证 3.对于区间如何确定的问题,我们只需要将l-1节点旋至根,r+1节点旋至根下即可 4.对于1~x或x~n区间的操作,我们还需要0和n+1这两个哨兵节点 #include

【Luogu】P3391文艺平衡树(Splay)

题目链接 ddosvoid和自为风月马前卒教了我这道题 他们好强啊 如果我们要反转区间[l,r] 我们首先把l的前驱旋转到根节点 再把r的后继旋转到根节点的右儿子 那么此时根节点的右儿子的左儿子所代表的就是区间l,r 具体为啥不知道 然后可以给splay的节点打标记,就像线段树一样 inline void pushdown(int x){ if(!tree[x].tag) return; swap(tree[x].e[0],tree[x].e[1]); tree[tree[x].e[0]].ta

P3391 【模板】文艺平衡树(Splay)新板子

P3391 [模板]文艺平衡树(Splay) 题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,

AC日记——文艺平衡树 洛谷 P3391

文艺平衡树 思路: splay翻转操作模板: 虚拟最左最右端点,然后每次都把l翻转到root,r+2翻转到root的右节点: 然后在r+2的左节点上打标记: 标记需要在旋转,rank,print时下放: 建树需要用完全平衡二叉树: 来,上代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define m

P3391 【模板】文艺平衡树(Splay)

题目背景 这是一道经典的Splay模板题--文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n?1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r

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

题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r

洛谷P3391 【模板】文艺平衡树(Splay)(FHQ Treap)

题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r

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

题目背景 这是一道经典的Splay模板题--文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是 (1,2,?n?1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1≤l≤r≤n 输出格式: 输出一行n个数字,表示原始序列经过m次变换后的结果