description

codeforces

给一棵\(n\)个节点的树,每次等概率选择树中剩下边的一条进行缩边,这条边的两个端点有相同的概率被保留,求最后每个点被留下的概率。

data range

\[n\le 50
\]

solution

感谢Mr_Spade的博客教会了我做这道题。

考虑一下这个缩边的过程:依次选择\(n-1\)条边进行缩边,缩边时可以选择这条边连接的两条节点中的任意一个保留,最后只剩下一个节点。

可以发现在这个过程中我们并不知道最后剩下的到底是哪个节点,因此我们选择钦定最后留下的节点,每次有和它相连的边进行收缩时总是它保留下来,问题转化为求钦定的一个节点最后留下的概率。

另外,这个过程的方案数是有限的,可以知道是\(2^{n-1}(n-1)!\)。那么这道题实际上变成了一道计数问题,我们只要求出使钦定的一个节点最后留下的方案数,最后再除以总方案数即可。

将钦定最后留下的点看成整棵树的根节点,我们考虑树形\(DP\)。

子树的状态应该是忽略子树外的所有边,当前子树的根节点保留下来的方案数,那么我们可以知道由子树转移到父亲过程的表现如下:

1.根节点转移到父亲节点(父亲节点被根节点所替代);

2.在缩掉父亲节点和儿子节点之间的连边之前,缩掉了一些其他的边;

3.缩掉了父亲节点和儿子节点之间的连边,根节点转移到儿子节点。

由子树转移到父亲的过程就是已知到达步骤3的方案数推知到达步骤1的方案数的过程。

由于在根节点转移到父亲节点后,我们还有可能缩掉一些其他的边,

可以知道缩掉其他边的方案数 和 在根节点转移到父亲节点后,父节点剩下的边数有关

那么我们显然需要在记录子数的基础上再记录一维表示边数。

设\(f_{u,i}\)表示根节点\(rt\)转移到节点\(u\),子数\(u\)内还剩下\(i\)条边的方案数,答案即\(f_{rt,n-1}\)。

这个状态实际上包含了两种边排列的方案:一类是此时已经缩掉的边,一类是此时还剩下的\(i\)条边。

枚举其儿子节点\(v\),如何添加新子树\(v\)?

对于一个完整的根节点从\(u->v\)的过程,我们还要知道在根节点转移到\(v\)时\(v\)子树内剩下的边数\(j\)。

我们已经考虑好了缩掉前面子树中边的情况,因此只需要考虑步骤2在子树\(v\)中究竟缩掉了多少条边。

设这个值为\(k\),那么根节点转移到\(u\)时\(v\)子树内剩下的边数为\(j+k\)。

设\(f'_u\)为转移后的数组\(f_u\),枚举\(i,j,k\),我们进行一下分类讨论:

\(k=0:\)根节点转移到\(u\)时不需要缩边即转移到根节点\(v\),说明\(u\)和\(v\)在根节点到达\(u\)之前就已经缩为一点。

\((u,v)\)这条边只能插入\(v\)的子树已经被缩掉的方案中,它在方案中可以放到最前或在第\((sz_v-1-i)\)条被缩掉的边之后(\(sz_v\)表示\(v\)的子树大小),因此插入这条边的方案数为\((sz_v-i)\);注意这条边可以随意选缩掉了哪个节点,于是方案数\(\times 2\);

然后考虑将\(v\)的子树的缩边方案合并到\(u\)的子树的缩边方案,注意只能是已经缩掉的边相互合并,还未缩掉的边相互合并,于是方案数为\(\binom{i+j}{j}\binom{sz_u-i-1+sz_v-j}{sz_v-j}\)。

转移方案即\(f'_{u,i+j+k+1}+=f_{u,i}\times 2(sz_v-j)\binom{i+j}{j}\binom{sz_u-i-1+sz_v-j}{sz_v-j}f_{v,j}\)

\(k>0:\)根节点转移到\(u\)后继续在子树\(v\)中缩了\(k\)条边,最后转移到根节点\(v\)。

注意\((u,v)\)这条边的缩边时间和选择保留的点都已经被钦定(在继续缩\(k\)条边之后,只能保留根节点),因此直接使用\(f'_{u,i+j+k+1}+=f_{u,i}\times\binom{i+j}{j}\binom{sz_u-i-1+sz_v-j}{sz_v-j}f_{v,j}\)转移方案。

实现时可以存一个\(g_i\)表示\(DP\ f_{u,i}\)后乘的一大堆东西,这样总复杂度为\(O(n^4)\)。

Code

#include<bits/stdc++.h>
#define FILE "CF1060F"
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const int N=52;
il ll read(){
RG ll data=0,w=1;RG char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
return data*w;
}
il void file(){
srand(time(NULL)+rand());
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
} int n,head[N],nxt[N<<1],to[N<<1],cnt;
il void add(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}
int sz[N];dd fac[N],f[N][N],g[N],tmp[N];
il dd C(int n,int m){return fac[n]/fac[m]/fac[n-m];}
void dfs(int u,int ff){
f[u][0]=sz[u]=1;
for(RG int i=head[u];i;i=nxt[i]){
RG int v=to[i];if(v==ff)continue;
dfs(v,u);memset(g,0,sizeof(g));
for(RG int j=0;j<=sz[v];j++)
for(RG int k=0;k<=j;k++)
if(j>k)g[j]+=f[v][k];
else if(j==k)g[j]+=2*(sz[v]-j)*f[v][k];
memset(tmp,0,sizeof(tmp));
for(RG int j=0;j<sz[u];j++)
for(RG int k=0;k<=sz[v];k++)
tmp[j+k]+=f[u][j]*g[k]*C(j+k,j)*C(sz[u]-j-1+sz[v]-k,sz[u]-j-1);
memcpy(f[u],tmp,sizeof(f[u]));
sz[u]+=sz[v];
}
} int main()
{
n=read();fac[0]=1;
for(RG int i=1;i<=n;i++)fac[i]=fac[i-1]*i*2;
for(RG int i=1,u,v;i<n;i++){
u=read();v=read();add(u,v);add(v,u);
}
for(RG int i=1;i<=n;i++){
memset(f,0,sizeof(f));
dfs(i,0);
printf("%.10lf\n",f[i][n-1]/fac[n-1]);
}
return 0;
}

[CF1060F]Shrinking Tree的更多相关文章

  1. [CF1060F]Shrinking Tree[树dp+组合计数]

    题意 你有一棵 \(n\) 个点的树,每次会随机选择树上的一条边,将两个端点 \(u,v\) 合并,新编号随机为 \(u,v\).问最后保留的编号分别为 \(1\) 到 \(n\) 的概率. \(n\ ...

  2. Codeforces 1060 F. Shrinking Tree

    题目链接 一道思维好题啊...感觉这种类型的题很检验基本功是否扎实(像我这样的就挂了). 题意:你有一棵\(n\)个点的树,每次随机选择一条边,将这条边的两个端点合并,并随机继承两个点标号中的一个,问 ...

  3. [Codeforces 1060F] Shrinking Tree

    Link: 传送门 Solution: 原来CF的官方题解也能鸽啊…… 详细题解 该题思路: 1.对于每个点删边方案数为$fac[n-1]$,总贡献为每种方案下满足的概率的和,接下来直接求贡献 2.每 ...

  4. Solution -「CF 1060F」Shrinking Tree

    \(\mathcal{Description}\)   Link.   给定一棵 \(n\) 个点的树,反复随机选取一条边,合并其两端两点,新点编号在两端两点等概率选取.问每个点留到最后的概率.    ...

  5. 一句话题解&&总结

    CF79D Password: 差分.两点取反,本质是匹配!最短路+状压DP 取反是套路,匹配是发现可以把操作进行目的化和阶段化,从而第二次转化问题. 且匹配不会影响别的位置答案 sequence 计 ...

  6. [CF]Round513

    A Phone Numbers 题意:定义"电话号码"为开头为'8',长度为11的字符串.给定一些字符,每个字符只能用一次,求可以拼出多少个电话号码(可以重复). 直接min(st ...

  7. Tree - Decision Tree with sklearn source code

    After talking about Information theory, now let's come to one of its application - Decision Tree! No ...

  8. Tree - XGBoost with parameter description

    In the previous post, we talk about a very popular Boosting algorithm - Gradient Boosting Decision T ...

  9. [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法

    二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...

随机推荐

  1. Spring Cloud 熔断机制 -- 断路器

    Spring Cloud 入门教程(七): 熔断机制 -- 断路器 对断路器模式不太清楚的话,可以参看另一篇博文:断路器(Curcuit Breaker)模式,下面直接介绍Spring Cloud的断 ...

  2. python import vs from import

    https://stackoverflow.com/questions/9439480/from-import-vs-import

  3. webpack中Development和Production模式的区分打包

    当我们在开发一个项目的时候,我们一般用development这个环境进行项目的开发,在这个环境下,webpack使用dev-server,帮我们启用一个服务器,然后这个服务器里面还集成了一些,比如hm ...

  4. Vs2015 遇到 CL:fatal error c1510 cannot load language clui.dll

    网上说什么点击修复VS,修改VS的,经验证都不好使,直接下载这个库,放在system32/64下面皆可以了

  5. Qt-第一个QML程序-3-自定义一个按钮

    项目基本信息前两个已经说了,这里直接放下运行截图, 对的,这里就是说上面的那个红色的按钮,这里需要了解Qml的动画和状态 这里先把整个按钮的代码写出来,一点一点写 Rectangle { id:clo ...

  6. python3 爬虫爬取深圳公租房轮候库(深圳房网)

    深圳公租房轮候库已经朝着几十万人的规模前进了,这是截至16年10月之前的数据了,贴上来大家体会下 所以17年已更新妥妥的10W+ 今天就拿这个作为爬虫的练手项目 1.环境准备: 操作系统:win10 ...

  7. Python3安装pywin32模块

    假如你安装的是Python3.6, 那么可以直接用PyCharm或者pip安装pywin32模块: 但是, 由于我安装的是Python3.7, 所以PyCharm或者pip都无法成功安装pywin32 ...

  8. [SHELL]结构化命令之条件语句

    1.if-then语句  #!/bin/bash username="root" if grep $username /etc/passwd then echo "the ...

  9. Java学习笔记-13.创建窗口和程序片

    1.init()方法:程序片第一次被创建,初次运行初始化程序片时调用. start()方法:每当程序片进入web浏览器中,并且允许程序片启动他的常规操作时调用(特殊的程序片被stop()关闭):同样在 ...

  10. EF中如何为表添加新的字段和映射

    首先先了解一下ef生成的模型edmx的代码,传送门:http://www.cnblogs.com/yushengbo/p/4807715.html 一.添加新的字段 例子就用我现在项目的这个吧,首先在 ...