SGU 187 - Twist and whirl -- want to cheat
原题地址: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相关推荐
- SGU 187.Twist and whirl - want to cheat( splay )
维护一个支持翻转次数M的长度N的序列..最后输出序列.1<=N<=130000, 1<=M<=2000 splay裸题... ------------------------- ...
- SGU 187 Twist and whirl - want to cheat(splay)
题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=187 题意:给出一个长度为n的数列,每次将某个区间的数字翻转.求最后的序列. 思路: ...
- 【Splay】[SGU 187]Twist and whirl - want to cheat
这道题真是我见过的最坑的平衡树的题目.... time limit per test: 0.25 sec. memory limit per test: 4096 KB 这个也太小了吧! 题目大意就是 ...
- sgu 187 Twist and whirl - want to cheat 伸展树(splay)
裸的区间翻转..直接写个splay就行... #include <iostream> #include <cstdio> #include <algorithm> ...
- SGU 187.Twist and whirl - want to cheat
splay 不过竟然用reverse一发水过了... 调用STL的代码:(156ms) #include <iostream> #include <cstdio> #inclu ...
- SGU 187 Twist and whirl - want to cheat
伸展树... 赤果果地抄袭雷哥的代码:http://www.cnblogs.com/jianglangcaijin/archive/2013/01/21/2869148.html 题意:输入n,m.给 ...
- 史上最全的SGU题目分类
由于SGU上神题遍地,特列此表,便于训练时分类训练. 101 Domino 欧拉路 102 Coprime 枚举/数学方法 103 Traffic Lights 最短路 104 Little Shop ...
- 《题目与解读》红书 训练笔记目录《ACM国际大学生程序设计竞赛题目与解读》
虽然2012年出版的老书了,但是是由三次世界冠军的上海交大ACM队出版的书籍,选择的题目是ACM经典中的经典,书中有非常详细的题解,可以学到很多东西,值得一刷. 目录 第一部分 第一章 数学 1.1 ...
- SGU 286 Ancient decoration(Euler路径+二分匹配)
http://acm.sgu.ru/problem.php?contest=0&problem=286 先找欧拉回路,再做二分匹配,输出匹配 有一道题和这个很像:HDU 3551 Hard P ...
最新文章
- php=与-,谈谈PHP中的 -、= 和 :: 符号
- [转]application.properties详解 --springBoot配置文件
- BDOC generated after customer product id is changed in CRM - CUST_MAT_INF
- 【H.264/AVC视频编解码技术】第六章【指数哥伦布编码】
- 表格过滤器_气缸选型其实并不复杂,知道这些再也不怕选错气缸(附计算表格)...
- CF Gym 101630 B Box
- 解决:您需要来自xxx的权限才能对此文件夹进行更改(电脑系统取得管理员权限)
- Python chr 函数 - Python零基础入门教程
- GridView动态添加模版列
- Linux 加入域的那些事儿!
- STM32控制WS2812B HAL库
- matlab freqz-m,Matlab函数freqs和freqz
- HMC510LP5ETR资料
- Android音频架构
- Gamma、Linear、sRGB 和Unity Color Space,你真懂了吗?
- Python多线程爬虫,主播信息资料爬取采集
- CSS空格和换行的处理
- 象棋对战js代码实现
- xshell打开多个窗口_打开软件太多,窗口用起来一团糟?让它来帮你管理:Groupy...
- hadoop-3.1.3 启动HDFS时报错ERROR: Attempting to operate on hdfs namenode as root的解决方法
热门文章
- 三、Web服务器——HTTP协议 Response对象 ServletContext对象 学习笔记
- python网络爬虫系列(四)——requests模块
- LeetCode 1816. 截断句子
- 数据仓库 Hive(内含大数据镜像下载)
- 天池 在线编程 Character deletion
- LeetCode 554. 砖墙(map计数)
- 程序员面试金典 - 面试题 10.05. 稀疏数组搜索(二分查找)
- LintCode 183. 木材加工(二分查找)
- LeetCode 85. 最大矩形(DP/单调递增栈,难)
- LeetCode 1016. 子串能表示从 1 到 N 数字的二进制串(bitset)