• SGU 187


    原题地址:http://acm.sgu.ru/problem.php?contest=0&problem=187

    太开心啦!!!!这道题从2013年开始困扰我!!今天晚上第四次下定决心把它写一写,之前写了三次(事实上是五个程序)都没有把它搞定,甚至无从查错……没想到今晚居然1A啦太激动了喵哈哈~我先去激动一会

    题目大意:给定一个数字n,构建一个从1 ~ n的初始数列,给出 m 个操作,每个操作对应两个数字 x 和 y 每次将当前序列中的第 x 位到第 y 位翻转,输出最终的序列

    数据范围和限制:1<=N<=130000, 1<=M<=2000, 时间限制0.25s, 内存限制 4M(这……)

    题目分析:这题貌似是改过数据范围或者内存限制,反正网上很多大牛直接建了一棵 n 的节点的伸展树,然后进行区间翻转,但是现在的数据范围必然导致MLE。但是这并不意味着伸展树无计可施我们需要另寻它路。hockey传授的解法是这样的:将每个区间视为一个点( 记作[l, r] ),在需要对它的子区间 [i, j] 进行翻转操作时,我们将它拆成三个点 [l, i]、[i, j]、[j, r],然后在[i, j]上打上翻转标记……

    一般地来讲就是这样:我们的Splay是由若干个区间组成的,当我们需要翻转区间[i, j]时,先查找出 i - 1 在当前树中的哪个区间上,然后将它拆成左右两个区间(约定我们拆分的左区间包含数i - 1)并记录左区间A,同样地,我们在树中查找 j 的位置并拆分,记录右区间B,将B旋转到根,再将A旋转为B的左儿子,则A的右子树就是待操作的区间,对其进行标记即可。

    下面贴出我的代码,尽可能使注释详细

      1 //date 20140119
      2 #include <cstdio>
      3 #include <cstring>
      4 
      5 inline int getint() //读入优化
      6 {
      7     int ans (0); char w = getchar();
      8     while('0' > w || '9' < w)w = getchar();
      9     while('0' <= w && w <= '9')
     10     {
     11         ans = ans * 10 + w - '0';
     12         w = getchar();
     13     }
     14     return ans;
     15 }
     16 
     17 inline int min(int a, int b){return a < b ? a : b;}
     18 inline int max(int a, int b){return a > b ? a : b;}
     19 
     20 int n, m;
     21 struct SPlay
     22 {
     23     struct node
     24     {
     25         int l, r, rev, revit, size; // l、r为区间左右端点,rev标记以当前节点为根的整棵子树是否被翻转,revit标记当前节点所代表的区间是否被翻转
     26         node *s[2], *p;
     27         int sum(){return r - l + 1;}
     28         int getlr(){return p->s[1] == this;}
     29         node(int ll, int rr){l = ll; r = rr; s[0] = s[1] = p = 0; rev = revit = 0; size = sum();}
     30         node *link(int w, node *p){s[w] = p; if(p)p->p = this; return this;}
     31         void update(){size = (s[0] ? s[0]->size : 0) + (s[1] ? s[1]->size : 0) + sum();}
     32         void pushdown()//旋转标记下放
     33         {
     34             if(rev)
     35             {
     36                 node *q = s[0]; s[0] = s[1]; s[1] = q;
     37                 if(s[0])s[0]->rev ^= 1;
     38                 if(s[1])s[1]->rev ^= 1;
     39                 revit ^= 1;
     40                 rev = 0;
     41             }
     42         }
     43     }*root;
     44     
     45     void rot(node *p)
     46     {
     47         node *q = p->p->p;
     48         p->getlr() ? p->link(0, p->p->link(1, p->s[0])) : p->link(1, p->p->link(0, p->s[1]));
     49         p->p->update();
     50         if(q)q->link(q->s[1] == p->p, p);else{root = p; p->p = 0;}
     51     }
     52     
     53     void splay(node *p, node *tar)
     54     {
     55         while(p->p != tar && p->p->p != tar)
     56             p->getlr() == p->p->getlr() ? (rot(p->p), rot(p)) : (rot(p), rot(p));
     57         if(p->p != tar)rot(p);
     58         p->update();
     59     }
     60     
     61     void preset(int l, int r){root = new node(l, r);}
     62     //以上是伸展树的基本操作,如有不熟悉可以参照我博客之前一篇介绍伸展树的文章
     63     int findKth(int k)//寻找当前序列的第k个数所在的区间,并将其旋转到根,返回值pos是指需要将找到的区间从该区间的第pos个数拆成两个区间
     64     {
     65         node *p = root;
     66         p->pushdown();
     67         while(!(((p->s[0] ? p->s[0]->size : 0) < k) && ((p->s[0] ? p->s[0]->size : 0) + p->sum() >= k)))//如果没有找到则继续找
     68         {
     69             if((p->s[0] ? p->s[0]->size : 0) >= k){p = p->s[0]; p->pushdown();}
     70             else{k -= (p->s[0] ? p->s[0]->size : 0) + p->sum(); p = p->s[1]; p->pushdown();}
     71         }
     72         k -= (p->s[0] ? p->s[0]->size : 0);//记录k在该区间中的实际位置,以便拆点
     73         splay(p, 0);
     74         return k;
     75     }
     76     
     77     void divide(node *p, int pos)//将节点p拆成两个节点,使拆解后的左区间包含恰好pos个数
     78     {
     79         p->pushdown();
     80         if(p->sum() == pos)return;//如果不需要拆,则不拆
     81         node *q1, *q2;
     82         if(p->revit)
     83         {
     84             q1 = new node(p->r - pos + 1, p->r); q1->revit = 1;
     85             q2 = new node(p->l, p->r - pos); q2->revit = 1;
     86         }
     87         else
     88         {
     89             q1 = new node(p->l, p->l + pos - 1);
     90             q2 = new node(p->l + pos, p->r);
     91         }
     92         q1->link(1, q2->link(1, p->s[1]));
     93         q1->link(0, p->s[0]);
     94         q2->update();
     95         q1->update();
     96         if(!p->p)root = q1;
     97         else p->p->link(p->getlr(), q1);
     98         delete p;
     99     }
    100     
    101     node *succ()//寻找当前根节点的后继,属于基础操作
    102     {
    103         root->pushdown();
    104         node *q = root->s[1];
    105         q->pushdown();
    106         while(q->s[0]){q = q->s[0]; q->pushdown();}
    107         splay(q, 0);
    108         return q;
    109     }
    110     void deal(int a, int b)//翻转区间[a,b]
    111     {
    112         int i = findKth(a - 1);//找到当前区间的前驱
    113         divide(root, i);
    114         node *p = root;
    115         int j = findKth(b);//找到当前区间的后继
    116         divide(root, j);
    117         node *q = succ();
    118         splay(p, q);
    119         p->s[1]->rev ^= 1;//进行标记
    120         p->s[1]->pushdown();
    121     }
    122     
    123     void print(node *p)//以下是一个中根便利进行输出
    124     {
    125         p->pushdown();
    126         if(p->s[0])print(p->s[0]);
    127         if(p->revit)for(int i = min(p->r, n); i >= max(1, p->l); --i)printf("%d ", i);
    128         else for(int i = max(1, p->l); i <= min(p->r, n); ++i)printf("%d ", i);
    129         if(p->s[1])print(p->s[1]);
    130     }
    131     
    132     void print()
    133     {
    134         print(root);
    135         printf("
    ");
    136     }
    137 }S;
    138 
    139 int main()
    140 {
    141     n = getint(); m = getint();
    142     S.preset(0, n + 1);//0和n + 1是哨兵节点,防止越界
    143 
    144     int x, y;
    145     for(int i = 1; i <= m; ++i)
    146     {
    147         x = getint(); y = getint();
    148         S.deal(x + 1, y + 1);//由于0的存在,第x个数实际上是第x + 1个数
    149     }    
    150     S.print();
    151     return 0;
    152 }

    小注:理解算法之后这道题就是锻炼编程能力了,希望能给在这道题卡住的同学们一点帮助吧

  • 相关阅读:
    leetcode(4) Median of Two Sorted Arrays
    logisitic回归
    共识算法 pos,Dpos
    solidity合约详解
    solidity中的memory和 storage详解
    win10下搭建私链
    区块链学习(7) 共识
    区块链学习(6)区块链
    区块链学习(5)比特币网络
    区块链学习(4)交易(二)
  • 原文地址:https://www.cnblogs.com/w007878/p/3526297.html
Copyright © 2020-2023  润新知