异象石(不用引理的乱搞做法)

本文介绍了在处理异象石问题时,采用的一种避免使用引理的解决方案。通过维护虚树结构,实现了在log时间内查询点的父亲和儿子节点。详细讲解了基础做法和正解,并提供了代码实现。最终证明了这种方法的正确性和时间复杂度。
摘要由CSDN通过智能技术生成

QAQ 237行的代码 QAQ

打了我一个下午啊啊啊啊。

题目

题目

我的做法

基础做法

机房大佬CLB提示我这道题目是虚树(当然我的做法不知道是不是虚树,反正虚就对了),于是我发现对于以下的图:在这里插入图片描述
在对外显示上,两个蓝点的LCA是可以代替这两个蓝点的,然后,我们的做法出来了,对于两个点,维护他们两个的LCA,如果一个虚点(即不是真的异象石的位置)只有一个点(不管实的虚的),那么就将这个点连到其父亲上面。

特别的,因为(个人觉得)难以维护目前树上没有父亲的点,所以默认 1 1 1点永远存在。(当然后来发现没有父亲的点最多一个,其实用个 r o o t root root存一下就行了)

当然,还是有不少细节的。

给上一个图(绿色为虚点,蓝色为实点,黑色为原树上的边,红色为虚树上的边):

在这里插入图片描述

注:一下内容的儿子、父亲、LCA可能有点难分辨是实树上的还是虚树上的,同时以下都以插入为例。

如何在log时间内查询一个点最近的父亲?

处理出DFS序后,我们新建了一个点,那么对于这个这个新建的点,如果是叶子节点的,那么整个子树(实树)直接认为其是父亲(用线段树实现),但是如果是非叶子节点,可以发现,他建立时最多只有两个儿子(虚树)节点(因为我们只要两个点有了LCA(实树)就建),然后把DFS序分成两段即可,但是需要注意的是,不一定是两个儿子,有时候只有一个儿子,因为LCA可能是其自己。

如何在log的时间内查询一个点的某个儿子的子树内的儿子节点编号

首先一个点的一个儿子(原树)的子树内的只可能存在一个儿子(虚树),两个的话他们必然会产生一个LCA,那么怎么查到这个点就是重中之重的事了,由于实树儿子的编号是固定的,我们可以在这个点的平衡树中插入其儿子的编号,而附加权值就是这个儿子子树中的虚树儿子编号。

实现

由于 1 1 1一直都在,所以 z a n s zans zans表示到 1 1 1点的距离,根据要不要经过 1 1 1决定加还是不加。(本人觉得 r o o t root root的实现会更简单吧)

当然只有亿点细节。

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),以及亿点点的常数,但也许是因为平衡树跑不满,或者因为正解用了set的缘故,我跑的竟然比正解快???

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
#define  N  110000
#define  M  210000
using  namespace  std;
typedef  long  long  LL;
//FHQ treap(平衡树)
int  val[M],ke1[M],ke2[M],tot,rt[M],siz[N],son[M][2];bool  li[N]/*这个点是否已经存在异象石*/;//最上层的点全部归到1 
void  spilt(int  now,int  k,int  &A,int  &B)
{
   
	if(!now)A=B=0;
	else
	{
   
		if(ke1[now]<=k)A=now,spilt(son[A][1],k,son[A][1],B);
		else  B=now,spilt(son[B][0],k,A,son[B][0]);
	}
}
int  mer(int  A,int  B)
{
   
	if(!A  ||  !B)return  A+B;
	if(val[A]<=val[B])son[A][1]=mer(son[A][1],B);
	else  son[B][0]=mer(A,son[B][0]),A^=B^=A^=B;
	return  A; 
}
inline  void  add(int  id,int  k1,int  k2)
{
   
	tot++;val[tot]=rand();ke1[tot]=k1;ke2[tot]=k2;
	int  x,y;spilt(rt[id],k1,x,y);
	rt[id]=mer(mer(x,tot),y);
}
inline  void  del(int  id,int  k)
{
   
	int  x,y,z;spilt(rt[id],k,x,z);spilt(x,k-1,x,y);
	rt[id]=mer(x,z);
}
inline  void  change(int  id,int  k1,int  k2)
{
   
	int  x,y,z;spilt(rt[id],k1,x,z);spilt(x,k1-1,x,y);
	ke2[y]=k2;
	rt[id]=mer(x,mer(y,z));
}
inline  int  findz(int  id,int  k)//这个子树是否存在虚树的儿子 
{
   
	int  bk=0;
	int  x,y,z;spilt(rt[id],k-1,x,y);spilt(y,k,y,z);
	if(y)bk=ke2[y];
	rt[id]=mer(mer(x,y),z);
	return  bk;
}
//线段树 
struct  TREE
{
   
	int  l,r,c;
}tr[M];int  tlen;
void  bt(int   l,int  r)
{
   
	int  now=+
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值