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. 利用Visual Studio寻找C#程序必要的运行库文件

    在工程打包中,有时候很头痛的就是运行所需要的库文件不能够全面的包含进来,特别是有时候调用了一系列外部扩展.对于这些问题,我们可以借用Visual Studio的打包功能帮助我们寻找软件运行必须的库文件 ...

  2. Raid1源代码分析--开篇总述

    前段时间由于一些事情耽搁了,最近将raid1方面的各流程整理了一遍.网上和书上,能找到关于MD下的raid1的文档资料比较少.决定开始写一个系列的关于raid1的博客,之前写过的一篇读流程也会在之后加 ...

  3. hdu 2818 Building Block (带权并查集,很优美的题目)

    Problem Description John are playing with blocks. There are N blocks ( <= N <= ) numbered ...N ...

  4. 【leetcode】Merge k Sorted Lists(按大小顺序连接k个链表)

    题目:Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity ...

  5. Qt使用QGraphicsView实现滑动窗体效果

    QGraphicsView用来显示一个滚动视图区的QGraphicsScene内容.QGraphicsScene提供了QGraphicsItem的容器功能.通常与QGraphicsView一起使用来描 ...

  6. Server.HTMLEncode用法

    Server.HTMLEncode用法!! Server.HTMLEncode HTMLEncode 一.HTMLEncode 方法对指定的字符串应用 HTML 编码. 语法 Server.HTMLE ...

  7. js获取get值

    //获取get值 function getPar(par) { //获取当前URL var local_url = document.location.href; //获取要取得的get参数位置 va ...

  8. 《第一行代码》学习笔记9-活动Activity(7)

    1.发现Android中的活动是可以层叠的,每启动一个新的活动,就会覆盖在原活动之上, 然后点击Back键会销毁最上面的活动,下面的一个活动就会重新显示出来. 2.Android是使用任务来管理活动的 ...

  9. Map基本用法

    Map的基本用法 map内部使用的是红黑树,在map内部所有的数据都是有序的 map插入有三种方法: insert(pair<int,string>(i,str)); myMap.inse ...

  10. 如何在WebSocket类中访问Session

    我最近正在做一个基于websocket的webQQ,最后代码会开源带github上,所以过程中我就不贴所有的代码啦~就贴问题的关键. 我在WebSocket里发消息的时候需要用到session,因为在 ...