原题地址: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("\n");
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 }

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

转载于:https://www.cnblogs.com/w007878/p/3526297.html

SGU 187 - Twist and whirl -- want to cheat相关推荐

  1. SGU 187.Twist and whirl - want to cheat( splay )

    维护一个支持翻转次数M的长度N的序列..最后输出序列.1<=N<=130000, 1<=M<=2000 splay裸题... ------------------------- ...

  2. SGU 187 Twist and whirl - want to cheat(splay)

    题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=187 题意:给出一个长度为n的数列,每次将某个区间的数字翻转.求最后的序列. 思路: ...

  3. 【Splay】[SGU 187]Twist and whirl - want to cheat

    这道题真是我见过的最坑的平衡树的题目.... time limit per test: 0.25 sec. memory limit per test: 4096 KB 这个也太小了吧! 题目大意就是 ...

  4. sgu 187 Twist and whirl - want to cheat 伸展树(splay)

    裸的区间翻转..直接写个splay就行... #include <iostream> #include <cstdio> #include <algorithm> ...

  5. SGU 187.Twist and whirl - want to cheat

    splay 不过竟然用reverse一发水过了... 调用STL的代码:(156ms) #include <iostream> #include <cstdio> #inclu ...

  6. SGU 187 Twist and whirl - want to cheat

    伸展树... 赤果果地抄袭雷哥的代码:http://www.cnblogs.com/jianglangcaijin/archive/2013/01/21/2869148.html 题意:输入n,m.给 ...

  7. 史上最全的SGU题目分类

    由于SGU上神题遍地,特列此表,便于训练时分类训练. 101 Domino 欧拉路 102 Coprime 枚举/数学方法 103 Traffic Lights 最短路 104 Little Shop ...

  8. 《题目与解读》红书 训练笔记目录《ACM国际大学生程序设计竞赛题目与解读》

    虽然2012年出版的老书了,但是是由三次世界冠军的上海交大ACM队出版的书籍,选择的题目是ACM经典中的经典,书中有非常详细的题解,可以学到很多东西,值得一刷. 目录 第一部分 第一章 数学 1.1 ...

  9. SGU 286 Ancient decoration(Euler路径+二分匹配)

    http://acm.sgu.ru/problem.php?contest=0&problem=286 先找欧拉回路,再做二分匹配,输出匹配 有一道题和这个很像:HDU 3551 Hard P ...

最新文章

  1. php=与-,谈谈PHP中的 -、= 和 :: 符号
  2. [转]application.properties详解 --springBoot配置文件
  3. BDOC generated after customer product id is changed in CRM - CUST_MAT_INF
  4. 【H.264/AVC视频编解码技术】第六章【指数哥伦布编码】
  5. 表格过滤器_气缸选型其实并不复杂,知道这些再也不怕选错气缸(附计算表格)...
  6. CF Gym 101630 B Box
  7. 解决:您需要来自xxx的权限才能对此文件夹进行更改(电脑系统取得管理员权限)
  8. Python chr 函数 - Python零基础入门教程
  9. GridView动态添加模版列
  10. Linux 加入域的那些事儿!
  11. STM32控制WS2812B HAL库
  12. matlab freqz-m,Matlab函数freqs和freqz
  13. HMC510LP5ETR资料
  14. Android音频架构
  15. Gamma、Linear、sRGB 和Unity Color Space,你真懂了吗?
  16. Python多线程爬虫,主播信息资料爬取采集
  17. CSS空格和换行的处理
  18. 象棋对战js代码实现
  19. xshell打开多个窗口_打开软件太多,窗口用起来一团糟?让它来帮你管理:Groupy...
  20. hadoop-3.1.3 启动HDFS时报错ERROR: Attempting to operate on hdfs namenode as root的解决方法

热门文章

  1. 三、Web服务器——HTTP协议 Response对象 ServletContext对象 学习笔记
  2. python网络爬虫系列(四)——requests模块
  3. LeetCode 1816. 截断句子
  4. 数据仓库 Hive(内含大数据镜像下载)
  5. 天池 在线编程 Character deletion
  6. LeetCode 554. 砖墙(map计数)
  7. 程序员面试金典 - 面试题 10.05. 稀疏数组搜索(二分查找)
  8. LintCode 183. 木材加工(二分查找)
  9. LeetCode 85. 最大矩形(DP/单调递增栈,难)
  10. LeetCode 1016. 子串能表示从 1 到 N 数字的二进制串(bitset)