首页 > 其他 > 详细

1483. [HNOI2009]梦幻布丁【平衡树-splay】

时间:2018-03-30 23:57:18      阅读:335      评论:0      收藏:0      [点我收藏+]

Description

N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.
例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

Input

第一行给出N,M表示布丁的个数和好友的操作次数. 
第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,
对于每个操作,
若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 
若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0。
n,m<=1000000

Output

针对第二类操作即询问,依次输出当前有多少段颜色.

Sample Input

4 3
1 2 2 1
2
1 2 1
2

Sample Output

3
1
 
调了一下午终于A掉了qwq……
学会启发式合并后,其实这个题就是裸题了
splay启发式合并,听起来很智能,只不过就是暴力将小splay上的点一个个insert到大splay上
这个题只需要将需要改的颜色的splay合并到目标颜色splay上就好了
(这个程序并没有从小的合并到大的,不过仍然很快,可能是数据太弱了)
只不过程序中有一些小细节需要注意就是了
细节详见code
 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N (1000000+100)
using namespace std;
int Size[N],Key[N];//key[i]中存的是编号为i的颜色,用序列的编号大小来建splay,节点里存颜色 
int Father[N],Son[N][2];
int n,m,Root[N],ans,a[N];
int flag;

int Get(int x){return Son[Father[x]][1]==x;}
void Update(int x){Size[x]=Size[Son[x][0]]+Size[Son[x][1]]+1;}
void Clear(int x){Key[x]=Father[x]=Son[x][0]=Son[x][1]=Size[x]=0;}

void Rotate(int x)
{
	int wh=Get(x);
	int fa=Father[x],fafa=Father[fa];
	Son[fa][wh]=Son[x][wh^1];
	Father[fa]=x;
	if (Son[fa][wh]) Father[Son[fa][wh]]=fa;
	Son[x][wh^1]=fa;
	Father[x]=fafa;
	if (fafa) Son[fafa][Son[fafa][1]==fa]=x;
	Update(fa);
	Update(x);
}

void Splay(int x,int &root)
{
	for (int fa;fa=Father[x];Rotate(x))
		if (Father[fa])
			Rotate(Get(fa)==Get(x)?fa:x);
	root=x;
}

void Insert(int x,int color,int &root)
{
	if (root==0)
	{
		Size[x]=1;
		Key[x]=color;
		root=x;
		return;
	}
	int now=root,fa=0;
	while (1)
	{
		fa=now;now=Son[now][x>now];
		if (now==0)
		{
			Size[x]=1;
			Key[x]=color;
			Father[x]=fa;
			Son[fa][x>fa]=x;
			Update(fa);
			Splay(x,root);
			return;
		}
	}
}

void Change(int x,int &root,int color)//唯一需要注意的函数,参数:(被拆的splay的当前节点,合并到的splay的根,需要改成什么颜色)。按中序遍历将要拆的splay一个个拆掉就好 
{
	if (Son[x][0]) Change(Son[x][0],root,color);
	
	int rson=Son[x][1];//因为下面要clear x这个节点的信息,所以事先存一下 
	int pre_color=Key[x];//同上 
	if (!flag && Key[x]!=Key[x-1]) flag=1+(color==Key[x-1]);//☆如果x是连续序列的开始,就把flag改成1。如果不仅是开始,还能和前一个连起来,flag再加一。(flag在连续序列结束的地方会用到) 
	Clear(x);
	Insert(x,color,root);//插入到目标splay中 
	if (Key[x]==Key[x+1]) ans-=1;//如果当前是连续序列的结束且这个点能和后面的颜色接上,ans--。 
	if (Key[x+1]!=pre_color) ans-=flag-1,flag=0;//如果这个点是结束,ans根据开头是否接上判断减不减。flag归零。 
	
	if (rson) Change(rson,root,color);
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		if (a[i]!=a[i-1]) ans++;
		Insert(i,a[i],Root[a[i]]);
	}
	int opt,x,y;
	for (int i=1;i<=m;++i)
	{
		scanf("%d",&opt);
		if (opt==1)
		{
		  scanf("%d%d",&x,&y);
		  if (Root[x]==0 || x==y) continue;//如果当前颜色没有节点  或者  这次更改前后的颜色是一样的 就不用做了(因为做了会卡) 
		  Change(Root[x],Root[y],y);
		  Root[x]=0;
		}
		if (opt==2) printf("%d\n",ans);
	}
}

 

1483. [HNOI2009]梦幻布丁【平衡树-splay】

原文:https://www.cnblogs.com/refun/p/8679228.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!