原题地址: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的右子树就是待操作的区间,对其进行标记即可。

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

 //date 20140119
#include <cstdio>
#include <cstring> inline int getint() //读入优化
{
int ans (); char w = getchar();
while('' > w || '' < w)w = getchar();
while('' <= w && w <= '')
{
ans = ans * + w - '';
w = getchar();
}
return ans;
} inline int min(int a, int b){return a < b ? a : b;}
inline int max(int a, int b){return a > b ? a : b;} int n, m;
struct SPlay
{
struct node
{
int l, r, rev, revit, size; // l、r为区间左右端点,rev标记以当前节点为根的整棵子树是否被翻转,revit标记当前节点所代表的区间是否被翻转
node *s[], *p;
int sum(){return r - l + ;}
int getlr(){return p->s[] == this;}
node(int ll, int rr){l = ll; r = rr; s[] = s[] = p = ; rev = revit = ; size = sum();}
node *link(int w, node *p){s[w] = p; if(p)p->p = this; return this;}
void update(){size = (s[] ? s[]->size : ) + (s[] ? s[]->size : ) + sum();}
void pushdown()//旋转标记下放
{
if(rev)
{
node *q = s[]; s[] = s[]; s[] = q;
if(s[])s[]->rev ^= ;
if(s[])s[]->rev ^= ;
revit ^= ;
rev = ;
}
}
}*root; void rot(node *p)
{
node *q = p->p->p;
p->getlr() ? p->link(, p->p->link(, p->s[])) : p->link(, p->p->link(, p->s[]));
p->p->update();
if(q)q->link(q->s[] == p->p, p);else{root = p; p->p = ;}
} void splay(node *p, node *tar)
{
while(p->p != tar && p->p->p != tar)
p->getlr() == p->p->getlr() ? (rot(p->p), rot(p)) : (rot(p), rot(p));
if(p->p != tar)rot(p);
p->update();
} void preset(int l, int r){root = new node(l, r);}
//以上是伸展树的基本操作,如有不熟悉可以参照我博客之前一篇介绍伸展树的文章
int findKth(int k)//寻找当前序列的第k个数所在的区间,并将其旋转到根,返回值pos是指需要将找到的区间从该区间的第pos个数拆成两个区间
{
node *p = root;
p->pushdown();
while(!(((p->s[] ? p->s[]->size : ) < k) && ((p->s[] ? p->s[]->size : ) + p->sum() >= k)))//如果没有找到则继续找
{
if((p->s[] ? p->s[]->size : ) >= k){p = p->s[]; p->pushdown();}
else{k -= (p->s[] ? p->s[]->size : ) + p->sum(); p = p->s[]; p->pushdown();}
}
k -= (p->s[] ? p->s[]->size : );//记录k在该区间中的实际位置,以便拆点
splay(p, );
return k;
} void divide(node *p, int pos)//将节点p拆成两个节点,使拆解后的左区间包含恰好pos个数
{
p->pushdown();
if(p->sum() == pos)return;//如果不需要拆,则不拆
node *q1, *q2;
if(p->revit)
{
q1 = new node(p->r - pos + , p->r); q1->revit = ;
q2 = new node(p->l, p->r - pos); q2->revit = ;
}
else
{
q1 = new node(p->l, p->l + pos - );
q2 = new node(p->l + pos, p->r);
}
q1->link(, q2->link(, p->s[]));
q1->link(, p->s[]);
q2->update();
q1->update();
if(!p->p)root = q1;
else p->p->link(p->getlr(), q1);
delete p;
} node *succ()//寻找当前根节点的后继,属于基础操作
{
root->pushdown();
node *q = root->s[];
q->pushdown();
while(q->s[]){q = q->s[]; q->pushdown();}
splay(q, );
return q;
}
void deal(int a, int b)//翻转区间[a,b]
{
int i = findKth(a - );//找到当前区间的前驱
divide(root, i);
node *p = root;
int j = findKth(b);//找到当前区间的后继
divide(root, j);
node *q = succ();
splay(p, q);
p->s[]->rev ^= ;//进行标记
p->s[]->pushdown();
} void print(node *p)//以下是一个中根便利进行输出
{
p->pushdown();
if(p->s[])print(p->s[]);
if(p->revit)for(int i = min(p->r, n); i >= max(, p->l); --i)printf("%d ", i);
else for(int i = max(, p->l); i <= min(p->r, n); ++i)printf("%d ", i);
if(p->s[])print(p->s[]);
} void print()
{
print(root);
printf("\n");
}
}S; int main()
{
n = getint(); m = getint();
S.preset(, n + );//0和n + 1是哨兵节点,防止越界 int x, y;
for(int i = ; i <= m; ++i)
{
x = getint(); y = getint();
S.deal(x + , y + );//由于0的存在,第x个数实际上是第x + 1个数
}
S.print();
return ;
}

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

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题目总结

    SGU还是个不错的题库...但是貌似水题也挺多的..有些题想出解法但是不想写代码, 就写在这里吧...不排除是我想简单想错了, 假如哪位神犇哪天发现请告诉我.. 101.Domino(2015.12. ...

  3. SGU 分类

    http://acm.sgu.ru/problemset.php?contest=0&volume=1 101 Domino 欧拉路 102 Coprime 枚举/数学方法 103 Traff ...

  4. cheat sheet (小抄的意思-考试的时候,带在路上原先抄的重要的知识点)

    Cheat Sheet,这里面有个Cheat(欺骗),想当然的话,意思肯定不好.事实上,这Cheat Sheet 的原意的确也就是“小抄”的意思.所以,字典的定义是:“A piece of paper ...

  5. 转:PostgreSQL Cheat Sheet

    PostgreSQL Cheat Sheet CREATE DATABASE CREATE DATABASE dbName; CREATE TABLE (with auto numbering int ...

  6. Git Cheat Sheet

    Merge Undo git merge with conflicts $ git merge --abort Archive $ git archive --format zip --output ...

  7. CSS3 Animation Cheat Sheet:实用的 CSS3 动画库

    CSS3 Animation Cheat Sheet 是一组预设的动画库,为您的 Web 项目添加各种很炫的动画.所有你需要做的是添加样式表到你的网站,为你想要添加动画效果的元素应用预制的 CSS 类 ...

  8. SGU 495. Kids and Prizes

    水概率....SGU里难得的水题.... 495. Kids and Prizes Time limit per test: 0.5 second(s)Memory limit: 262144 kil ...

  9. ce游戏内存修改器(Cheat Engine)

    ce修改器(Cheat Engine)一款专门修改内存修改编辑的游戏工具它包括16进制编辑,反汇编程序,内存查找工具新版6.1 版的CE与6.0 最大的区别就是添加了修改器制作工具,比之前 5.6.1 ...

随机推荐

  1. js之正则表达式(下)

    1.分组之exec返回数组 1>非分组匹配的exec返回数组: var pattern =/\d+[a-z]+/; var str='234google'; alert(pattern.exec ...

  2. 20145120 《Java程序设计》实验一实验报告

    20145120 <Java程序设计>实验一实验报告 实验名称:Java开发环境的熟悉 实验目的与要求: 1.使用JDK编译.运行简单的Java程序:(第1周学习总结) 2.编辑.编译.运 ...

  3. 模仿开发H5游戏,看你有多色

    开发记录 前言 之前跟着慕课网学习开发H5小游戏开心鱼,勾起我的兴趣. 在写代码的过程中,不怎么会遇到问题.虽然代码是亲手敲出来的,但是由于并没有对游戏的整体思路,所以并不知道开发与优化的过程. 为了 ...

  4. [resource]23个python的机器学习包

    23个python的机器学习包,从常见的scikit-learn, pylearn2,经典的matlab替代orange, 到最新最酷的Theano(深度学习)和torch 7 (well,其实lua ...

  5. 2007: [Noi2010]海拔 - BZOJ

    Description YT市是一个规划良好的城市,城市被东西向和南北向的主干道划分为n×n个区域.简单起见,可以将YT市看作一个正方形,每一个区域也可看作一个正方形.从而,YT城市中包括(n+1)× ...

  6. SVN 安装配置

    1,软件下载 到官方网站的下载二进制安装文件,来到二进制包下载部分,找到 Windows NT, 2000, XP and 2003部分,然后选择Apache 2.2 或者 Apache 2.4,这样 ...

  7. HDU 5486 Difference of Clustering 图论

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5486 题意: 给你每个元素一开始所属的集合和最后所属的集合,问有多少次集合的分离操作,并操作和不变操 ...

  8. 【HDOJ】【4089】Activation

    概率DP kuangbin总结中的第5题 题解copy: HDU 4098 题意:有n个人排队等着在官网上激活游戏.Tomato排在第m个. 对于队列中的第一个人.有一下情况: 1.激活失败,留在队列 ...

  9. 2-Highcharts曲线图之折线图

    示例图片,在网上下载一张图片如图:其中数据自定义 引入上节模版配置  在script标签中写代码:具体代码如下   信息将在代码中解释. 分析:“五省收益趋势”是标题: x坐标为[2011年-2016 ...

  10. Sqrt函数高效实现

    转自一个Sqrt函数引发的血案 我们平时经常会有一些数据运算的操作,需要调用sqrt,exp,abs等函数,那么时候你有没有想过:这个些函数系统是如何实现的?就拿最常用的sqrt函数来说吧,系统怎么来 ...