luogu P3224 [HNOI2012]永无乡

背景:

这道题调了近 4 h 4h 4h,脑子废了…

题目传送门:

https://www.luogu.org/problemnew/show/P3224

题意:

n n n个点,每个点一个权值两种操作。
1. 1. 1.合并两个岛。
2. 2. 2.询问当前联通块的权值的第 k k k小值所对应的岛屿。

思路:

S p l a y Splay Splay+启发式合并。
细节较多(比如 f a , s i z e fa,size fa,size的初值, r o o t root root的处理等),可以仔细找找。

代码:

#include
#include
#include
using namespace std;
	struct node{int bianhao,d,fa,c,son[2];} tr[200010];
	int root[200010];
	int fa[200010],size[200010];
	int n,m,q,len;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())
		if(ch=='-') f=-1;
	for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
	return x*f;
}
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
void ups(int x)
{
	int lc=tr[x].son[0],rc=tr[x].son[1];
	tr[x].c=tr[lc].c+tr[rc].c+1;
}
void add(int bianhao,int d,int fa)
{
	tr[++len]=(node){bianhao,d,fa,1,0,0};
}
void ins(int id,int bianhao,int d)
{
	root[id]=id;
	add(bianhao,d,0);
}
void rot(int x,int w)//左旋:0,右旋:1
{
	int y=tr[x].fa,yy=tr[y].fa;
	tr[y].son[1^w]=tr[x].son[w];
	if(tr[x].son[w]) tr[tr[x].son[w]].fa=y;
	tr[yy].son[tr[yy].son[0]==y?0:1]=x;
	
	tr[x].fa=yy,tr[x].son[w]=y,tr[y].fa=x;
	
	ups(y),ups(x);
}
void splay(int id,int x,int rt)
{
	while(tr[x].fa!=rt)
	{
		int y=tr[x].fa,yy=tr[y].fa;
		if(yy==rt)
		{
			rot(x,tr[y].son[0]==x?1:0);
			continue;
		}
		if(tr[yy].son[0]==y)
		{
			if(tr[y].son[0]==x) rot(y,1),rot(x,1); else rot(x,0),rot(x,1);
			continue;
		}
		if(tr[y].son[1]==x) rot(y,0),rot(x,0); else rot(x,1),rot(x,0); 
	}
	if(!rt) root[id]=x;
}
int findip(int x,int d)
{
	if(d==tr[x].d) return x;
	if(d<tr[x].d)
		return tr[x].son[0]?findip(tr[x].son[0],d):x;
	else
		return tr[x].son[1]?findip(tr[x].son[1],d):x;
}
int findnum(int x,int d)
{
	int lc=tr[x].son[0],rc=tr[x].son[1];
	if(tr[x].c<d) return -1;
	if(d<=tr[lc].c) return findnum(lc,d);
	else if(d>tr[lc].c+1) return findnum(rc,d-tr[lc].c-1);
	else return tr[x].bianhao;
}
void dfs(int x,int id)
{
	int lc=tr[x].son[0],rc=tr[x].son[1];
	if(lc) dfs(lc,id);
	int u=findip(root[id],tr[x].d);
	tr[x].son[0]=tr[x].son[1]=0;
	tr[x].fa=u;
	tr[x].c=1;
	tr[u].son[tr[x].d<tr[u].d?0:1]=x;
	ups(u);
	splay(id,x,0);
	if(rc) dfs(rc,id);
}
void merge(int x,int y)
{
	int t1=find(x),t2=find(y);
	if(t1==t2) return;
	if(size[t1]<size[t2]) swap(t1,t2);
	dfs(root[t2],t1);
	size[t1]+=size[t2];
	fa[t2]=t1;
}
int main()
{
	char s[5];
	int x,y;
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		fa[i]=i,size[i]=1;
	for(int i=1;i<=n;i++)
	{
		x=read();
		ins(i,i,x);
	}
	for(int i=1;i<=m;i++)
	{
		x=read(),y=read();
		merge(x,y);
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%s",s+1);
		x=read(),y=read();
		if(s[1]=='B') merge(x,y); else printf("%d\n",findnum(root[find(x)],y));
	}
}

你可能感兴趣的:(#,splay,#,启发式合并)