区间翻转 bzoj 3223 文艺平衡树 (splay)
2017-05-26 17:20
323 查看
【bzoj3223】Tyvj 1729 文艺平衡树
Description您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
Input
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数 接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
Output
输出一行n个数字,表示原始序列经过m次变换后的结果
Sample Input
5 3
1 3
1 3
1 4
Sample Output
4 3 2 1 5
HINT
N,M<=100000
思路
区间翻转问题
翻转区间->交换BST的左右子树(逐层交换,正确性请自行验证),区间标记降低翻转次数
寻找要操作的区间[l,r]:将当前排名(size)为l的节点(节点l-1)转到根,将当前排名为r+2的节点(节点r+1)转到根的右子树的根节点,则根的右子树的根节点的左子树为所求区间,直接标记该区间就可以了。(类似线段树区间修改)
splay操作维护一棵较优的树,三种方式进行旋转操作。
注意
1.标记是在每一次访问到一个新的节点是就要pushdown的(改变树的结构会破坏标记区间,所以先一步下传标记)
2.区分一个节点的排名和这个节点的值:这个节点的排名是它是当前数组中的第几个,用左儿子的size+1表示;这个节点的值是题目中输入的数字,在本题中是1~n
3.增加数字为0和n+1的两个哨兵节点,因为如果对区间1~x或x~n操作,用到前后的节点就需要0和n+1。
4.有些读者会疑惑,难道交换左右子树不会破坏BST的性质吗?这就是容易混淆的一点,我们的区间操作是根据下标翻转的,用数组实现时下标就是数组地址,子树交换时,只是存储内容的改变,下标位置(树的形状)只会在旋转时改变,保证BST性质。
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; const int MAXN = 100010; const int INF = 0x7fffffff; #define lson tr[x].ch[0] #define rson tr[x].ch[1] struct Node{ int ch[2], fa, v, size; bool rv;//是否翻转,因为偶数次翻转无效,所以可以用bool,位运算 void set(int x){v=x; ch[0]=ch[1]=fa=rv=0; size=1;} }tr[MAXN]; int n, m, root, tot, l, r, x1, x2; void updata(int x){//维护size if( !x ) return; tr[x].size = 1 + tr[lson].size + tr[rson].size; } void pushdown(int x){//打标记必备 if( !x ) return; if( tr[x].rv ){//x的子节点集合为翻转区间 tr[lson].rv ^= 1; tr[rson].rv ^= 1;//标记下传 tr[x].rv = 0;//消除 int t = lson; lson = rson; rson = t;//当前层翻转 } } inline bool son(int x) {return tr[tr[x].fa].ch[1] == x;}//判断ls,rs void link(int x, int y, bool cc){//建立x与y的新关系 tr[x].ch[cc] = y; tr[y].fa = x; } void rotate(int x){//一种重新连边似的旋转方式 int fa = tr[x].fa, ffa = tr[fa].fa, tt = son(x); link(ffa,x,son(fa)); link(fa,tr[x].ch[!tt],tt); link(x,fa,!tt); updata(fa); } void splay(int x, int f){ pushdown(x); if(f == 0) root = x;//l-1到root while(tr[x].fa != f){//到目标位置 if (tr[tr[x].fa].fa == f) {rotate(x); break;}//离目标位置只有一步 if (son(x) == son(tr[x].fa)) {rotate(tr[x].fa); rotate(x);}//与fa形成一条链(链状是一种不优的状态,所以通过先旋fa再旋x的方式降深度) else {rotate(x); rotate(x);}//Z字形(由于旋上去自动就会降深度,所以就是普通旋转) } updata(x); } int build(int l, int r){ if(l>r) return 0; int x = ++tot; int mid = (l+r) >> 1; tr[x].set(mid); lson = build(l, mid-1); rson = build(mid+1, r); tr[lson].fa = tr[rson].fa = x; updata(x); return x; } int find(int x, int y){ pushdown(x);//有标记就必须pushdown,且写成递归的形式 if (tr[lson].size+1 < y) return find(rson, y-tr[lson].size-1); else if(tr[lson].size+1 == y) return x; else return find(lson, y); } void dfs(int x){ pushdown(x);//标记在每一次访问之前都要pushdown if(tr[x].ch[0]) dfs(tr[x].ch[0]); if(tr[x].v<=n && tr[x].v>=1) cout<<tr[x].v<<' '; if(tr[x].ch[1]) dfs(tr[x].ch[1]); } int main(){ scanf("%d%d", &n, &m); tot = root = 0; tr[0].v = tr[n+1].v = INF; root = build(0, n+1);//0和n+1哨兵节点 while(m--){ scanf("%d%d", &l, &r); x1 = find(root, l); x2 = find(root, r+2);//找到l-1,r+1的下标(因为有一个边界0,所以第l个数其实是l-1,第r+2个数是r-1) splay(x1,0); splay(x2,x1); updata(root);//旋转l-1到root,r+1到root->rs,标记位置为root->rs->ls此位置及其以下的内容就是l~r tr[tr[x2].ch[0]].rv ^= 1; updata(tr[x2].ch[0]); updata(x2); updata(root); } dfs(root);//中序遍历 return 0; }
相关文章推荐
- splay区间翻转(bzoj 3223: Tyvj 1729 文艺平衡树)
- bzoj3223[Tyvj 1729] 文艺平衡树(splay模板题:区间翻转)
- bzoj 3223: Tyvj 1729 文艺平衡树(splay 模板题 区间翻转)
- splay处理区间翻转例题【BZOJ 3223】 Tyvj 1729 文艺平衡树
- [SPLAY维护区间][BZOJ 3223][TYVJ 1729]文艺平衡树
- splay区间翻转bzoj 3223(tyvj 1729)文艺平衡树题解
- bzoj3223 Tyvj 1729 文艺平衡树(Splay Tree+区间翻转)
- bzoj3223 文艺平衡树(平衡树区间翻转)
- BZOJ 3223 Splay区间翻转
- BZOJ 3223 Splay区间翻转
- bzoj3223 Tyvj 1729 文艺平衡树(Splay Tree+区间翻转)
- splay 新模板 【bzoj3223】 文艺平衡树
- 【BZOJ3223】文艺平衡树,Splay反转区间
- BZOJ 3223 文艺平衡树 [codevs3303翻转区间]
- 【Splay】bzoj3223 Tyvj 1729 文艺平衡树
- bzoj 1552: [Cerc2007]robotic sort && bzoj 3506: [Cqoi2014]排序机械臂(splay区间翻转)
- bzoj3223 Tyvj 1729 文艺平衡树 (splay)
- 【BZOJ3223】文艺平衡树(splay)
- BZOJ3223 文艺平衡树(Splay)
- 【splay】BZOJ 3223 文艺平衡树