小 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 选点,所有上一次剩下的点被选中的概率 必然会增加,因为这次不选的话,下次选点的边界往右移了,新人笑老人哭了,(再不放纵一把 就没法保证连通了。
- 对于每个点 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 两种情况。
- 等概率随机选出点对 (u,v) 若两点连通就跳过,不连通就连上。
根据树的定义,我们可以发现根本没必要随机两个数,因为所有点最终都会连在一颗树上,不妨直接把 1 看作根结点,把其它点看作散点,每次操作就是从树上随机选取一个点与散点匹配,这样操作起来绝对比什么连来连去在合并啥的舒服多了。
而我们还是看概率,会发现每一次选入一个点进树,树上每个点被选中的概率都会减少,散点中每个点被选中的概率都会增多,因此我们很好地发现这个性质与 1 似乎有点相像?
其实不 (然,就是这样!虽然小 Y 选点的时候,是不按规律的,但是我们可以强制让他按规矩办事 (直接规定从 1 开始枚举 u ) 就会发现,每一次选点的概率是一样的,只不过是值域不同而已。似乎不大好与 1 区分的亚子,所以我们先看到 4 吧。
- 随机选取一个 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());
}
}