【BZOJ3678】wangxz与OJ Splay

【BZOJ3678】wangxz与OJ

Description

某天,wangxz神犇来到了一个信息学在线评测系统(Online Judge)。由于他是一位哲♂学的神犇,所以他不打算做题。他发现这些题

目呈线性排列,被标记为1~n号,每道题都有一个难度值(可以<=0)。他决定与这些题目玩♂耍。

1、他可以在某个位置插♂入一些难度值特定的题目。

2、他可以吃♂掉(删除)一段题目。

3、他可以查询某个位置的题目的难度值。

维护一个初始有n个元素的序列(标记为1~n号元素),支持以下操作:

0 p a b (0<=p<=当前序列元素个数) (a<=b) 在p位置和p+1位置之间插入整数:a,a+1,a+2,...,b-1,b。若p为0,插在序列最前面;

1 a b (1<=a<=b<=当前序列元素个数) 删除a,a+1,a+2,...,b-1,b位置的元素;

2 p (1<=p<=当前序列元素个数) 查询p位置的元素。

Input

输入第一行包括两个正整数n(1<=n<=20000),m(1<=m<=20000),代表初始序列元素个数和操作个数。

接下来n个整数,为初始序列元素。

接下来m行,每行第一个为整数sym,

若sym=0,接下来有一个非负整数p,两个整数a,b;

若sym=1,接下来有两个正整数a,b;

若sym=2,接下来有一个正整数p;

p、x、y的含义及范围见题目描述。

在任何情况下,保证序列中的元素总数不超过100000。

保证题目涉及的所有数在int内。

Output

对每个sym=2,输出一行,包括一个整数,代表询问位置的元素。

Sample Input

5 3
1 2 3 4 5
0 2 1 4
1 3 8
2 2

Sample Output

2

题解:容易想到用Splay,我们的Splay的每个节点处维护的不是一个点,而是连续的一段数[a,b]。我们需要实现一个split操作:如果我们想将c和c+1分开,那么我们先找到c所在的节点,将它的后继提上来,并在二者之间新建一个点,将当前节点变成[a,c],并将新点变成[c+1,b]即可。特别地,如果c==b,则不用操作。

其余的操作就很容易了。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
struct node
{
	int ch[2],a,b,fa,siz;
}s[200010];
int n,m,tot,rt;
inline void pushup(int x)
{
	s[x].siz=s[s[x].ch[0]].siz+s[s[x].ch[1]].siz+s[x].b-s[x].a+1;
}
int build(int l,int r)
{
	if(l>r)	return 0;
	int x=(l+r)>>1;
	s[x].ch[0]=build(l,x-1),s[x].ch[1]=build(x+1,r);
	if(s[x].ch[0])	s[s[x].ch[0]].fa=x;
	if(s[x].ch[1])	s[s[x].ch[1]].fa=x;
	pushup(x);
	return x;
}
inline void rotate(int x,int &k)
{
	int y=s[x].fa,z=s[y].fa,d=(x==s[y].ch[1]);
	if(z)	s[z].ch[y==s[z].ch[1]]=x;
	else	if(y==k)	k=x;
	s[x].fa=z,s[y].fa=x,s[y].ch[d]=s[x].ch[d^1];
	if(s[x].ch[d^1])	s[s[x].ch[d^1]].fa=y;
	s[x].ch[d^1]=y;
	pushup(y),pushup(x);
}
inline void splay(int x,int &k)
{
	while(x!=k)
	{
		int y=s[x].fa,z=s[y].fa;
		if(y!=k)
		{
			if((x==s[y].ch[0])^(y==s[z].ch[0]))	rotate(x,k);
			else	rotate(y,k);
		}
		rotate(x,k);
	}
}
int find(int x,int y)
{
	if(!x)	return 0;
	if(y>s[s[x].ch[0]].siz&&y<=s[s[x].ch[0]].siz+s[x].b-s[x].a+1)	return x;
	if(s[s[x].ch[0]].siz>=y)	return find(s[x].ch[0],y);
	return find(s[x].ch[1],y-s[s[x].ch[0]].siz-s[x].b+s[x].a-1);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
inline void split(int y)
{
	int x=find(rt,y+1);
	splay(x,rt),splay(find(rt,s[s[x].ch[0]].siz+s[x].b-s[x].a+2),s[x].ch[1]);
	if(s[s[x].ch[0]].siz+s[x].b-s[x].a==y)	return ;
	int z=++tot;
	s[z].b=s[x].b,s[x].b=s[x].a+y-s[s[x].ch[0]].siz,s[z].a=s[x].b+1;
	s[z].fa=s[x].ch[1],s[s[x].ch[1]].ch[0]=z;
	pushup(z),pushup(s[x].ch[1]),pushup(x);
	rotate(z,s[x].ch[1]);
}
int main()
{
	n=rd(),m=rd();
	int i,x,y,a,b,op;
	for(i=1;i<=n;i++)	s[i+1].a=s[i+1].b=rd();
	rt=build(1,n+2),tot=n+2;
	for(i=1;i<=m;i++)
	{
		op=rd();
		if(op==0)
		{
			a=rd(),split(a);
			x=++tot,s[x].a=rd(),s[x].b=rd(),s[x].fa=s[rt].ch[1],s[s[rt].ch[1]].ch[0]=x;
			pushup(x),pushup(s[rt].ch[1]),pushup(rt);
		}
		if(op==1)
		{
			a=rd(),b=rd(),split(a-1),split(b);
			x=find(rt,a),splay(x,rt),y=find(rt,b+2),splay(y,s[x].ch[1]);
			s[y].ch[0]=0;
			pushup(y),pushup(x);
		}
		if(op==2)
		{
			a=rd(),x=find(rt,a+1),splay(x,rt);
			printf("%d\n",a-s[s[x].ch[0]].siz+s[x].a);
		}
	}
	return 0;
}//5 3 1 2 3 4 5 0 2 1 4 1 3 8 2 2
时间: 2024-10-13 04:50:32

【BZOJ3678】wangxz与OJ Splay的相关文章

bzoj3678: wangxz与OJ Splay缩点

RT. #include<bits/stdc++.h> #define L(t) (t)->c[0] #define R(t) (t)->c[1] #define F(t) (L(t)->s+1) #define G(t) (L(t)->s+(t)->u) #define M (l+r>>1) struct node{ int v,s,u; node* c[2]; node(int v,int s,int u,node* a,node* e) :v(v

BZOJ3678: wangxz与OJ

3678: wangxz与OJ Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 123  Solved: 23[Submit][Status] Description 某天,wangxz神犇来到了一个信息学在线评测系统(Online Judge).由于他是一位哲♂学的神犇,所以他不打算做题.他发现这些题 目呈线性排列,被标记为1~n号,每道题都有一个难度值(可以<=0).他决定与这些题目玩♂耍. 1.他可以在某个位置插♂入一些难度值特定的题目. 2

BZOJ 3678 wangxz与OJ 缩点Splay

题目大意 维护一个序列,支持 1. 插入一段序列,这个序列以1递增 2. 删除连续的一段序列 3. 查询位置p的数是多少. 思路 简单Splay维护就可以.但是后来好像被卡了,还有rope什么乱搞的都被卡了.于是观察这个插入的序列,他是一个很有规律的数列,但是插入之后我们却不一定查找这个序列中的数字,我们可以将这个数列当成一个节点插入Splay中去,这样每个节点可以记录l和r来表示这个点所代表的序列是什么.当要使用一个节点的时候,将这个节点从一个连续的序列中分裂出来在使用. CODE #defi

大神刷题表

9月27日 后缀数组:[wikioi3160]最长公共子串 dp:NOIP2001统计单词个数 后缀自动机:[spoj1812]Longest Common Substring II [wikioi3160]最长公共子串 [spoj7258]Lexicographical Substring Search 扫描线+set:[poj2932]Coneology 扫描线+set+树上删边游戏:[FJOI2013]圆形游戏 结论:[bzoj3706][FJ2014集训]反色刷 最小环:[poj1734

《数据结构》C++代码 Splay

       Splay,伸展树.之所以先写这个课内并不怎么常用的数据结构,是因为本人非常喜欢Splay,我觉得这是非常有美感且灵活的一种平衡树.在此先声明,我的伸展树写法来源于CLJ大牛,基础好的同学可以去他的博客中看看他的Splay实现模板,我的实现仅仅借鉴了CLJ大神的一点实现技巧而已.我的博文<心中的大牛博客列表>中有CLJ大神的博客链接.        还有很多同学可能并不了解Splay的思想,那么可以去看sqybi的文章<The magical splay>,百度文库就

[SCOI2014]方伯伯的OJ(线段树)

方伯伯正在做他的Oj.现在他在处理Oj上的用户排名问题.Oj上注册了n个用户,编号为1-n",一开始他们按照编号排名. 方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号: 1.操作格式为1 x y,意味着将编号为x的用户编号改为y,而排名不变,执行完该操作后需要输出该用户在队列中的位置,数据保证x必然出现在队列中,同时,1是一个当前不在排名中的编号. 2.操作格式为2 x,意味着将编号为x的用户的排名提升到第一位,执行完该操作后需要输出执行该操作前编号为x用户的排名. 3.操作格式

LeetCode OJ - Sum Root to Leaf Numbers

这道题也很简单,只要把二叉树按照宽度优先的策略遍历一遍,就可以解决问题,采用递归方法越是简单. 下面是AC代码: 1 /** 2 * Sum Root to Leaf Numbers 3 * 采用递归的方法,宽度遍历 4 */ 5 int result=0; 6 public int sumNumbers(TreeNode root){ 7 8 bFSearch(root,0); 9 return result; 10 } 11 private void bFSearch(TreeNode ro

LeetCode OJ - Longest Consecutive Sequence

这道题中要求时间复杂度为O(n),首先我们可以知道的是,如果先对数组排序再计算其最长连续序列的时间复杂度是O(nlogn),所以不能用排序的方法.我一开始想是不是应该用动态规划来解,发现其并不符合动态规划的特征.最后采用类似于LRU_Cache中出现的数据结构(集快速查询和顺序遍历两大优点于一身)来解决问题.具体来说其数据结构是HashMap<Integer,LNode>,key是数组中的元素,所有连续的元素可以通过LNode的next指针相连起来. 总体思路是,顺序遍历输入的数组元素,对每个

LeetCode OJ - Surrounded Regions

我觉得这道题和传统的用动规或者贪心等算法的题目不同.按照题目的意思,就是将被'X'围绕的'O'区域找出来,然后覆盖成'X'. 那问题就变成两个子问题: 1. 找到'O'区域,可能有多个区域,每个区域'O'都是相连的: 2. 判断'O'区域是否是被'X'包围. 我采用树的宽度遍历的方法,找到每一个'O'区域,并为每个区域设置一个value值,为0或者1,1表示是被'X'包围,0则表示不是.是否被'X'包围就是看'O'区域的边界是否是在2D数组的边界上. 下面是具体的AC代码: class Boar