随机树生成器

小 Y 有四种方法造树。

要想分辨不同的方法造出的树,自然要试着模拟然后统计四个树的特征。

以下四个标号分别代表四种方案:

由于性质比较特殊 ( 2 的范围比1更小 ) 所以我决定先讲 2。

2.对于每个点 i ,概率与 1≤j≤⌊(i−1)/2⌋   1≤j≤⌊2i−1​⌋连边。

事实上很简单。

我们强制要求所有树以 11 为根节点且强制要求这个树按值域分类要求比根节点小的在左子树,比根大的在右边,由于是全排列,不存在等于,可以安心许多。

由于每个点 i 都不可能与小于 (i-1)/2 的点连边,因此会发现所有大于 i 的点都不可能与 i 不可能连到的点连边,因此我们可以发现 2 是无法与 1 连边的。3 要在右子树只能与 1 连边,且 n 必然为它所在子树的最右节点,这样有用的性质对我们就大大增多了。因为我们可以发现每到一个 i 选点,所有上一次剩下的点被选中的概率 必然会增加,因为这次不选的话,下次选点的边界往右移了,新人笑老人哭了,(再不放纵一把 就没法保证连通了。

  1. 对于每个点 i ,概率与 1≤j≤ (i-1)/2  ≤j≤i−1连边。

相信大家都能发现 1 与 2 基本是同母异父 (悲 的亲兄弟了。经过我上面一番慷慨乱扯,可以发现 1 与 2 的性质就差在 (i-1)/2⌋∼−1⌊2i​⌋∼i−1 与 i 有无连边了,这里必然不能鸡蛋里挑骨头地去想是不是可能用 1 生成时所有点都刚好选在了 1∼⌊ (i-1)/2⌋1∼⌊2i​⌋ 里,想都不要想,这是纯属犯欠。 (放我那是要被数学老师和科学老师挑起来打的。 而我们会发现这样子选点的话,一条路走到黑,或是雨露均沾,就是链或菊花图等极端情况概率是特别特别小的,不妨遇到那种深度很大的树直接丢给 3,4 两种情况。

  1. 等概率随机选出点对 (u,v) 若两点连通就跳过,不连通就连上。

根据树的定义,我们可以发现根本没必要随机两个数,因为所有点最终都会连在一颗树上,不妨直接把 1 看作根结点,把其它点看作散点,每次操作就是从树上随机选取一个点与散点匹配,这样操作起来绝对比什么连来连去在合并啥的舒服多了。

而我们还是看概率,会发现每一次选入一个点进树,树上每个点被选中的概率都会减少,散点中每个点被选中的概率都会增多,因此我们很好地发现这个性质与 1 似乎有点相像?

其实不 (然,就是这样!虽然小 Y 选点的时候,是不按规律的,但是我们可以强制让他按规矩办事 (直接规定从 1 开始枚举 u ) 就会发现,每一次选点的概率是一样的,只不过是值域不同而已。似乎不大好与 1 区分的亚子,所以我们先看到 4 吧。

  1. 随机选取一个 n 个点的树。

好家伙,方法 4 直接放飞自我了。我们可以发现方案 4 唯一的性质就是平等,无论树的深度,无论任意两点,连边的概率都是一样的。但转念一想,菊花图,链等深度特别小或是特别大的图,好像都可以直接丢给4来背黑锅?

这一看,4 貌似就可爱多了呢。

事实上,这就是一道概率题吧。 (成这样也无话可说。)

做的时候也是一头雾水对着自己打出来的表懵逼半天 (最后还是靠老师指点才过的 ,实在是太屑了。

程序

#include<bits/stdc++.h>
#include<cassert>
using namespace std;
typedef vector<int> VI; 
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) 
{
	ll res=1;
	a%=mod;
	for(;b;b>>=1)
	{
		if(b&1)  
			res=res*a%mod;
		a=a*a%mod; 
	}
	return res;
}
const int N=1000;
int sz[N+10],d[N+10],dis[N+10],q[N+10],cnt[N+10],com,u,v,tc;
vector<int> e[N+10];
double c0;
void dfs2(int u,int f) 
{
	sz[u]=1;
	for(register int i=0;i<(int)(e[u].size());++i) 
	{
		int v=e[u][i];
		if(v==f) 
			continue;
		dfs2(v,u); 
		sz[u]+=sz[v];
	}
	if(sz[u]<N) 
		c0+=log(sz[u])+log(N-sz[u]);
}
int id;
int sep23(int c1,int c2)
{
	if(c1<=23) 
		return 2;
	if(c1>=27)
		 return 3;
	if(c1==24) 
	{
		if(c2>=3)
			 return 2; 
		else 
			return 3;
	}
	if(c1==25) {
		if(c2>=6) 
			return 2; 
		else 
			return 3;
	}
	if(c1==26) {
		if(c2>=9) 
			return 2; 
		else
			return 3;
	}
	return 0;
}
int check()
{
	c0=0;
	dfs2(1,0);
	for(register int i=1;i<N+1;++i)
		d[i]=(int)(e[i].size());
	int t=0;
	memset(dis,0,sizeof(dis));
	memset(cnt,0,sizeof(cnt));
	for(register int i=1;i<N+1;++i)
		if (d[i]==1)
		{
			q[t++]=i;
			cnt[dis[i]=1]++;
		}
	for(register int i=0;i<t;++i)
		for(register int j=0;j<(int)(e[q[i]].size());++j)
		{
			int v=e[q[i]][j];
			if((--d[v])==1) 
			{
				q[t++]=v;
				dis[v]=dis[q[i]]+1; 
				cnt[dis[v]]++;
			}
		}
	int c1=*max_element(dis+1,dis+N+1);
	int c2=0; 
	for(register int i=1;i<=c1;++i)
		c2+=(cnt[i]<=2); 
    
	if (c0<=7800) 
		return 1;
     
	if (id==1) 
		return 2;		
	if (id==2) 
		return sep23(c1,c2);
	if (id==3) 
	{
		if (c0>=8178) 
			return 4;
		if (c0>=8155)
		{
			if (c1<38)
				return 4;
			else
				return 3;
		}
		return 3;
	}
	if (id==4||id==5) 
	{ 
		if (c0>=8178) 
			return 4;
		if (c0>=8155) 
		{
			if (c1<38) 
				return 4;
			else 
				return 3;
		}
		return sep23(c1,c2);
	}
	return 0;
}
int main()
{
	scanf("%d",&id); 
	for(scanf("%d",&com);com;--com)
	{
		for(register int i=1;i<=N;++i) 
			e[i].clear();
		for(register int i=1;i<N;++i)
		{
			scanf("%d%d",&u,&v);
			e[u].push_back(v);
			e[v].push_back(u);
		}
		printf("%d\n",check());
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值