Problem Description
YaoYao is fond of playing his chains. He has a chain containing n diamonds on it. Diamonds are numbered from 1 to n. At first, the diamonds on the chain is a sequence: 1, 2, 3, …, n. He will perform two types of operations: CUT a b c: He will first cut down the chain from the ath diamond to the bth diamond. And then insert it after the cth diamond on the remaining chain. For example, if n=8, the chain is: 1 2 3 4 5 6 7 8; We perform “CUT 3 5 4”, Then we first cut down 3 4 5, and the remaining chain would be: 1 2 6 7 8. Then we insert “3 4 5” into the chain before 5th diamond, the chain turns out to be: 1 2 6 7 3 4 5 8.
FLIP a b: We first cut down the chain from the ath diamond to the bth diamond. Then reverse the chain and put them back to the original position. For example, if we perform “FLIP 2 6” on the chain: 1 2 6 7 3 4 5 8. The chain will turn out to be: 1 4 3 7 6 2 5 8
He wants to know what the chain looks like after perform m operations. Could you help him?
 
Input
There will be multiple test cases in a test data.
For each test case, the first line contains two numbers: n and m (1≤n, m≤3*100000), indicating the total number of diamonds on the chain and the number of operations respectively. Then m lines follow, each line contains one operation. The command is like this: CUT a b c // Means a CUT operation, 1 ≤ a ≤ b ≤ n, 0≤ c ≤ n-(b-a+1). FLIP a b    // Means a FLIP operation, 1 ≤ a < b ≤ n. The input ends up with two negative numbers, which should not be processed as a case.
 
Output
For each test case, you should print a line with n numbers. The ith number is the number of the ith diamond on the chain.
 
Sample Input
8 2
CUT 3 5 4
FLIP 2 6
-1 -1
 
Sample Output
1 4 3 7 6 2 5 8
 
 
题意:起初整个排列是1,2,,,N,然后有两种操作
CUT a b c 将[a,b]分离出来后插入到剩余的数的第c个位置之后
FLIP a b 将[a,b]翻转
求最后的排列。
 
解析:伸展树合并分裂。对于CUT操作,先把左边和右边的分裂出来,再合并在一起,再分成[1,c]和[c+1,k](k是剩余的个数,注意区间可能为空,特殊处理一下就好了),
再将[a,b]插入进去。对于FLIP操作,还是分裂,再将[a,b]翻转。最后将所有翻转标记下压。记录答案即可。
 
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int INF=1e9+;
const int maxn=;
int N,M,ans,A[maxn],cnt; //A数组保存数,cnt是节点标号,我是用数组模拟的
struct treap
{
treap* son[]; //左右儿子
int v,s,rev;
treap(){ v=s=rev=; son[]=son[]=NULL; }
treap(int nv);
int rk(){ return son[]->s+; } //排名,第几个数
int cmp(int k) //比较,如果相等返回-1,小于返回0,大于1
{
if(k==rk()) return -;
return k<rk()?:;
}
void pushup(){ s=son[]->s+son[]->s+; } //更新大小
void pushdown(); //处理懒惰标记
}null,tr[maxn];
treap::treap(int nv)
{
v=nv;
s=;
rev=;
son[]=son[]=&null;
}
void treap::pushdown()
{
if(this==&null) return;
if(rev)
{
swap(son[],son[]);
son[]->rev^=;
son[]->rev^=;
rev=;
}
}
treap* NewNode(int x)
{
tr[cnt]=treap(x);
return tr+cnt++;
}
struct splaytree
{
int Size;
treap* root;
splaytree(){ Size=; root=&null; }
void Rotate(treap* &t,int d) //翻转操作
{
t->pushdown();
treap* p=t->son[d^];
p->pushdown();
t->son[d^]=p->son[d];
p->son[d]=t;
t->pushup();
t=p;
t->pushup();
}
void Splay(treap* &t,int k) //将第k大的节点伸展到根
{
t->pushdown();
int d=t->cmp(k);
if(d!=-)
{
if(d) Splay(t->son[d],k- t->rk());
else Splay(t->son[d],k);
Rotate(t,d^);
}
t->pushup();
}
void Build(treap* &t,int le,int ri) //将N个数建成一棵树
{
if(le>ri) return;
int mid=(le+ri)/;
t=NewNode(mid);
Build(t->son[],le,mid-);
Build(t->son[],mid+,ri);
t->pushup();
}
void Cut(treap* &t,int a,int b,int c)
{
int len=b-a+;
if(len==N) return; //是整个区间就不用管了
Splay(t,a); t->pushdown(); //分裂出左边的
treap *L=t->son[];
L->pushdown();
t->son[]=&null; t->pushup(); Splay(t,len); t->pushdown(); //分裂出右边的
treap *R=t->son[];
R->pushdown();
t->son[]=&null; t->pushup(); treap *nt;
if(R!=&null) //左右合并
{
nt=R;
Splay(nt,);
nt->son[]=L; nt->pushup();
}
else
{
nt=L;
Splay(nt,a-);
nt->son[]=R; nt->pushup();
}
if(c+len==N) //在整个之后特殊处理一下就好
{
Splay(nt,c);
Splay(t,);
t->son[]=nt;
t->pushup();
return;
}
Splay(nt,c+);
treap *l=nt->son[]; l->pushdown();
nt->son[]=&null; nt->pushup();
t->son[]=nt; t->pushup();
Splay(t,);
t->son[]=l; t->pushup();
}
void Reverse(treap* &t,int a,int b) //翻转
{
Splay(t,a); //左边
treap *L=t->son[];
L->pushdown();
t->son[]=&null; t->pushup();
Splay(t,b-a+); //右边
treap *R=t->son[];
R->pushdown();
t->son[]=&null; t->pushup();
t->rev^=; //置翻转标记
t->pushdown();
t->son[]=L; t->pushup();
Splay(t,b);
t->son[]=R; t->pushup();
}
void PushAll(treap* &t) //中序遍历
{
if(t==&null) return;
t->pushdown();
PushAll(t->son[]);
A[++ans]=t->v;
PushAll(t->son[]);
t->pushup();
}
};
int main()
{
while(scanf("%d%d",&N,&M)!=EOF)
{
if(N<&&M<) break;
splaytree spt; cnt=;
spt.Build(spt.root,,N); //建树
int a,b,c;
char op[];
while(M--)
{
scanf("%s",op);
if(op[]=='C') //CUT操作
{
scanf("%d%d%d",&a,&b,&c);
spt.Cut(spt.root,a,b,c);
}
else //FLIP操作
{
scanf("%d%d",&a,&b);
spt.Reverse(spt.root,a,b);
} }
ans=;
spt.PushAll(spt.root); //整个下压
for(int i=;i<=ans;i++)
printf("%d%c",A[i],i==ans?'\n':' ');
}
return ;
}

Hdu3487-Play with Chain(伸展树分裂合并)的更多相关文章

  1. [HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)

    题目链接: https://www.luogu.org/problemnew/show/P2824 题目描述: 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在 ...

  2. 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)

    线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...

  3. [BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)

    解法一:二分答案+线段树 首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别. 这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案.先二分 ...

  4. Splay 伸展树

    废话不说,有篇论文可供参考:杨思雨:<伸展树的基本操作与应用> Splay的好处可以快速分裂和合并. ===============================14.07.26更新== ...

  5. [SinGuLaRiTy] SplayTree 伸展树

    [SinGuLaRiTy-1010]Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. Some Method Are Reprinted Fr ...

  6. Splay伸展树入门(单点操作,区间维护)附例题模板

    Pps:终于学会了伸展树的区间操作,做一个完整的总结,总结一下自己的伸展树的单点操作和区间维护,顺便给未来的自己总结复习用. splay是一种平衡树,[平均]操作复杂度O(nlogn).首先平衡树先是 ...

  7. K:伸展树(splay tree)

      伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...

  8. wikioi 1396 伸展树(两个模板)

    题目描写叙述 Description Tiger近期被公司升任为营业部经理.他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来 ...

  9. UVA 11922 Permutation Transformer —— splay伸展树

    题意:根据m条指令改变排列1 2 3 4 … n ,每条指令(a, b)表示取出第a~b个元素,反转后添加到排列尾部 分析:用一个可分裂合并的序列来表示整个序列,截取一段可以用两次分裂一次合并实现,粘 ...

随机推荐

  1. iOS7 UI适配教程

    最近写了点iOS7适配的文章,请指正 ios6to7 1 ios6to7 2

  2. poj 2566 Bound Found(尺取法 好题)

    Description Signals of most probably extra-terrestrial origin have been received and digitalized by ...

  3. IIS PHP 配置 问题总结

    今天帮助朋友解决一个IIS配置PHP的问题.大概是这样子的. IIS 与 PHP配置好了之后不能訪问,出现例如以下错误: HTTP 错误 500.19 - Internal Server Error ...

  4. leetcode_question_57 Insert Interval

    Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessa ...

  5. [Angular 2] Injecting a Service

    Using Services in Angular 2 is very simple. This lesson covers how to create a simple class as a Ser ...

  6. JavaScript 函数方法 - bind()

    Function.prototype.bind() ECMAScript5中新增的方法,但是在ECMAScript3可以通过模仿实现其方法作用 作用: bind() 方法会创建一个新函数,当这个新函数 ...

  7. TravelCMS旅游网站系统诞生记-1(后台框架篇)

  8. hive函数总结-日期函数

    获取当前UNIX时间戳函数: unix_timestamp语法: unix_timestamp() 返回值: bigint说明: 获得当前时区的UNIX时间戳举例: hive> select u ...

  9. C#重写Equals方法步骤

    检查传入的参数是否为null, 如果为null,那么返回false, 否则执行步骤2 调用ReferenceEquals查看是否为统一个对象,如果是,那么返回true, 否则执行步骤3 判断两者是否为 ...

  10. arry()数组的理解及api的使用(二)

    注意:本文都来自于w3school中文网,如果需要完整版请去--http://www.w3school.com.cn/jsref/jsref_obj_array.asp 1.1 slice() 方法- ...