UVA_11922 Permutation Transformer 【splay树】

一、题目

UVA11922

二、分析

为什么会有伸展树?

伸展树与AVL的区别除了保持平衡的方式不同外,最重要的是在每次查找点时,让该点旋转到根结点,这里可以结合计算机里的局部性原理思考。

伸展树有什么优势?

有了伸展树,我们可以根据每次到根节点的值,根据二叉搜索树的性质,可以将整棵树划分成两个部分,左子树的值都比根结点值大,右子树的值都比根结点小。

该题除了伸展树还用到了什么?

该题还需要旋转,所以,需要像线段树的lazy标记一样标记是否需要旋转。

有什么需要注意的地方?

一定注意写法的不同,构建树的范围不同,如果没有定于null数组,则需要将建树范围从$[1,n]$扩为$[0,n]$,因为空指针的sum(表示以该结点为根的树的大小)是不清楚的。

三、AC代码

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 using namespace std;
  6 
  7 struct Node
  8 {
  9     Node *ch[2];
 10     int sum, val;
 11     int flip;
 12     Node(int v = 0)
 13     {
 14         val = v;
 15         sum = 1; 
 16         flip = 0; 
 17         ch[0] = ch[1] = NULL;
 18     }
 19     int cmp(int x)
 20     {
 21         int d = x - (ch[0] == NULL ? 0 : ch[0]->sum);
 22         if(d == 1)  return -1;
 23         else    return d <= 0 ? 0 : 1;
 24     }
 25     void maintain()
 26     {
 27         sum = 1;
 28         if(ch[0] != NULL)   sum += ch[0]->sum;
 29         if(ch[1] != NULL)   sum += ch[1]->sum;
 30     }
 31     void pushdown()
 32     {
 33         if(flip) 
 34         {
 35             flip = 0;
 36             swap(ch[0], ch[1]);     //**
 37             if(ch[0] != NULL)
 38                 ch[0]->flip = !ch[0]->flip;
 39             if(ch[1] != NULL)
 40                 ch[1]->flip = !ch[1]->flip;
 41         }
 42     }
 43 };
 44 
 45 void build(Node* &o, int l, int r)
 46 {
 47     if(l > r)
 48         return;
 49     int mid = (l + r) >> 1;
 50     o = new Node(mid);
 51     build(o->ch[0], l, mid - 1);
 52     build(o->ch[1], mid + 1, r);
 53     o->maintain();
 54 }
 55 
 56 void rotate(Node* &o, int d)
 57 {
 58     Node *k = o->ch[d^1];
 59     o->ch[d^1] = k->ch[d];
 60     k->ch[d] = o;
 61     o->maintain();
 62     k->maintain();
 63     o = k;
 64 }
 65 
 66 
 67 void splay(Node* &o, int k)
 68 {
 69     o->pushdown();
 70     int d = o->cmp(k);
 71     if(d == 1)
 72         k -= (o->ch[0] == NULL ? 0 : o->ch[0]->sum) + 1;
 73     //当前结点不是第k个,则需要往上伸展
 74     if(d != -1)
 75     {
 76         Node *p = o->ch[d];
 77         p->pushdown();
 78 
 79         int d2 = p->cmp(k);
 80         int k2 = (d2 == 0 ? k : k - (p->ch[0] == NULL ? 0 :p->ch[0]->sum)  - 1);
 81         if(d2 != -1)
 82         {
 83             splay(p->ch[d2], k2);
 84             //x,x的父节点,x祖父结点三点共线
 85             if(d == d2) rotate(o, d^1);
 86             //不共线
 87             else    rotate(o->ch[d], d);
 88         }
 89         rotate(o, d^1);
 90     }
 91 }
 92 
 93 
 94 void print(Node* &o)    //一定要传引用
 95 {
 96     o->pushdown();
 97     if(o->ch[0] != NULL)    print(o->ch[0]);
 98     if(o->val)
 99         printf("%d\n", o->val);
100     if(o->ch[1] != NULL)    print(o->ch[1]);
101 
102     delete o;
103     o = NULL;
104 }
105 
106 Node* merge(Node* left, Node* right)
107 {
108     //left不能为NULL
109     //因为初始化了sum为1,对于空指针的sum,不清楚大小
110     //如何避免:建树从0开始,即保证sum>0
111     splay(left, left->sum);
112     left->ch[1] = right;
113     left->maintain();
114     return left;
115 }
116 
117 
118 void split(Node *o,  int k, Node* &left, Node* &right)
119 {
120     splay(o, k);
121     left = o;
122     right = o->ch[1];
123     o->ch[1] = NULL;
124     left->maintain();
125 }
126 
127 
128 int main()
129 {
130     //freopen("input.txt", "r", stdin);
131     //freopen("out.txt", "w", stdout);
132     int N, M, a, b;
133     while(scanf("%d %d", &N, &M)==2)
134     {
135         Node *root = NULL;
136         Node *left, *mid, *right, *o;
137         //上述模板注意不能用空指针
138         //必须从0开始,不然会RE
139         build(root, 0, N);
140 
141         for(int i = 0; i < M; i++)
142         {
143             scanf("%d %d", &a, &b);
144             //树里面有0所以是a
145             split(root, a, left, o);
146             split(o, b - a + 1, mid, right);
147             mid->flip ^= 1;
148             root = merge(merge(left, right), mid);
149         }
150         print(root);
151     }
152     return 0;
153 }

 

转载于:https://www.cnblogs.com/dybala21/p/10725777.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值