zoukankan      html  css  js  c++  java
  • 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
    ", 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 }
  • 相关阅读:
    【转载】Perl异常处理方法总结
    (个人)读取A.CSV修改它的某列,写入B.CSV
    (转载)PERL 处理CSV
    (转载)CSV 文件处理 PERL
    string.split()
    反转字符串应该注意的问题
    jQuery取值相加
    ADO.NET的五个主要对象
    Response.Redirect和Server.Transfer
    string和stringBuilder的区别
  • 原文地址:https://www.cnblogs.com/dybala21/p/10725777.html
Copyright © 2011-2022 走看看