小练习,splay区间反转

8 篇文章 0 订阅
6 篇文章 0 订阅

一道codevs的区间翻转问题。

http://codevs.cn/problem/1743/

题意,每次找最左边的数,令为k:   k=1,结束;  否则,ans++,翻转[1,k].

(给定的是全排列,所以,加两个点就可以直接做了。)


题目中说可能无解,但我觉得应该有解,所以就没管-1.   求大神指导一下到底是否一定有解。

(当然最好定义一个较大的操作数,大于此操作数仍无法结束程序,输出-1)


以下简要分析:

①两个虚拟点是极有用的,有了这两个虚拟点就避免讨论l=1时l-1找不到的情况。

②(我用的指针)插入的时候,一直向最右边插就可以了(记得每次把插入的点旋到根)~~~PS:我傻逼了,splay如果旋完我直接return 了,忘记如果旋到根要变root的指向。

③指针的操作就是不容易直接记录每个点的位置(好像可以直接找)(管它的,我写复杂了~) 所以我还用了size存左子数个数,每次找排名第2的(有虚拟节点),把它的值找到,是1结束,否则翻转。

其实我这样看似多余,实则是有必要的----  你每次直接调用地址进行操作,它上面可能有lazy标记没有变化,那么你操作完就有可能偏离了正常的答案,因此就需要从上往下依次找,每层下放lazy.

④pushdown是在每次往下查询的时候都要用到的,只要需要从根往下找,每层pushdown以下,操作类似于线段树。


之后就是常规的splay就可以了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 300000+20
using namespace std;
int n;
int cnt=0;
struct node
{
	node *f;
	node *ch[2];
	int key;
	int lazy;
	int size;
}S[maxn];
node *root;
void pushup(node *u)
{
	u->size=1;
	if(u->ch[0])u->size+=u->ch[0]->size;
	if(u->ch[1])u->size+=u->ch[1]->size;
}
void pushdown(node *u)
{
	if(u==NULL||!u->lazy)return ;
	if(u->ch[0])u->ch[0]->lazy^=1;
	if(u->ch[1])u->ch[1]->lazy^=1;
	node *y;
	y=u->ch[0];
	u->ch[0]=u->ch[1];
	u->ch[1]=y;
	u->lazy=0;
}
void rotate(node *u)
{
	node *f=u->f;
	if(f==NULL)return ;
	pushdown(u);
	pushdown(f);
	node *ff=f->f;
	int d=u==f->ch[1];
	int dd=0;
	if(ff!=NULL)dd=f==ff->ch[1];
	
	if(u->ch[d^1])u->ch[d^1]->f=f;
	f->ch[d]=u->ch[d^1];
	
	f->f=u;
	u->ch[d^1]=f;
	
	if(ff!=NULL)ff->ch[dd]=u;
	u->f=ff;
	pushup(f);
	pushup(u);
}
void splay(node *u,node *p)
{
	pushdown(u);
	while(u->f!=p)
	{
		node *f=u->f;
		node *ff=f->f;
		if(ff==p)
		{
			rotate(u);
			break;
		}
		int d=u==f->ch[1];
		int dd=f==ff->ch[1];
		if(d==dd)rotate(f);
		else rotate(u);
		rotate(u);
	}
	if(p==NULL)root=u;
	pushup(u);
}

void insert(int key)
{
	if(root==NULL)
	{
		root=&S[++cnt];
		root->lazy=0;
		root->key=key;
		root->size=1;
		root->ch[0]=root->ch[1]=root->f=NULL;
		return ;
	}
	node *u=root;
	node *y;
	while(1)
	{
		u->size++;
		/*if(key<u->key)
		{
			if(u->ch[0])u=u->ch[0];
			else
			{
				y=&S[++cnt];
				y->lazy=0;
				y->ch[0]=y->ch[1]=NULL;
				y->size=1;
				y->key=key;
				y->f=u;
				u->ch[0]=y;
				break;
			}
		}
		else
		{*/
			if(u->ch[1])u=u->ch[1];
			else
			{
				y=&S[++cnt];
				y->lazy=0;
				y->ch[0]=y->ch[1]=NULL;
				y->key=key;
				y->size=1;
				y->f=u;
				u->ch[1]=y;
				break;
			}
		
	}
	splay(y,NULL);
}
node * find(int k)
{
	node *u=root;
	while(1)
	{
		pushdown(u);
		int size=1;
		if(u->ch[0])size+=u->ch[0]->size;
		if(size==k)return u;
		if(k<size)u=u->ch[0];
		else 
		{
			k-=size;
			u=u->ch[1];
		}
	}
}
int find2()
{
	return find(2)->key;
}
void flip(int l,int r)
{
	//l,r+2
	node *ll=find(l);
	node *rr=find(r+2);
	splay(ll,NULL);
	splay(rr,root);
	root->ch[1]->ch[0]->lazy^=1;
}
void dfs(node *u)
{
	if(u->ch[0])dfs(u->ch[0]);
	printf("%d ",u->key);
	if(u->ch[1])dfs(u->ch[1]);
}
int main()
{
	scanf("%d",&n);
	root=NULL;
	int x;
	insert(0);
	for(int i=1;i<=n;i++)
	{		
		scanf("%d",&x);
		insert(x);
	}	
	insert(n+1);
	int ans=0;
	while(1)
	{
		int k=find2();
		if(k==1)break;
		ans++;
		flip(1,k);
	}
	printf("%d\n",ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值