Loading

随机造树方法

1.随机父亲(深度 \(\Theta(\log n)\)

树以 \(1\) 为根,则 \(\forall i\in[2,n]\),在 \([1,i)\) 中随机选择一个点作为自身父亲:

namespace RandomTreeGenerator{
	mt19937 rnd(time(0));
	void generateRandomTree(int n){
		int x;for(int i=2;i<=n;++i) x=rnd()%(i-1)+1,Edge(x,i);
	}
}

树的结点个数 \(n\) 与造出的树的平均深度 \(\times (5\times 10^3)\) 的关系如图中黑线:

图中红线为 \(y=(5\times 10^3)\times 1.6\log x\) 的函数图像,可以看出在 \(n\le 5\times 10^5\) 时这种造树方法得到树的平均深度约为 \(1.6\log n\)

2.Prüfer 序列(深度\(\Theta(\sqrt{n})\)

众所周知,一个长度为 \(n-2\)Prüfer 序列唯一对应着一棵无根树,所以我们可以随机生成一个 Prüfer 序列,将其按模板题的方法转化成树。

namespace RandomTreeGenerator{
	mt19937 rnd(time(0));
	int d[N],p[N];
    void generateRandomTree(int n){
        for(int i=1;i<=n;++i) d[i]=1;
        for(int i=1;i<=n-2;++i) ++d[p[i]=rnd()%n+1];
        int now,pre;for(int i=1;i<=n;++i) if(d[i]==1){pre=now=i;break;}
        for(int i=1;i<=n-2;++i){
            int f=p[i];Edge(now,f);
            if(--d[f]==1&&f<pre) now=f;
            else{while(d[++pre]!=1);now=pre;}
        }
        Edge(now,n);
    }
}

树的结点个数 \(n\) 与造出的树以 \(1\) 为根时的平均深度 \(\times 100\) 的关系如图中黑线:

图中红线为 \(y=100\times 2.5\sqrt{x}\) 的函数图像,可以看出在 \(n\le 5\times 10^5\) 时这种造树方法得到树的平均深度约为 \(2.5\sqrt{n}\)

3.左偏树(硬核)

指定一个 \([1,n]\) 中的常数 \(k\),把 \([1,k]\) 按顺序排成一条 \(1\) 在最上方的链,然后 \(\forall i\in (k+1,n]\),随机选择 \([i-k,i)\) 中的一个结点作为父亲。

造出树的深度下界为 \(\Omega\left(k+\frac{n}{k}\right)\)(本人口胡,不一定准确,但经过实际测试大概是这样的)。感性理解,造出链后要使深度尽量小,最优方案便是把所有结点平均接到链中每个点的下方,像这样:

此时树的深度为 \(\Theta\left(k+\frac{n}{k}\right)\),所以随机情况下树的深度为 \(\Omega\left(k+\frac{n}{k}\right)\)

namespace RandomTreeGenerator{
	#include <cassert>
	mt19937 rnd(time(0));
	const int k=3;
	void generateRandomTree(int n){
		assert(k<=n);
		for(int i=2;i<=k;++i) Edge(i,i-1);
		for(int i=k+1;i<=n;++i) Edge(i,rnd()%k+i-k);
	}
}
posted @ 2022-07-20 11:25  Albertvαn  阅读(446)  评论(0编辑  收藏  举报