题目链接

一道思维好题啊...感觉这种类型的题很检验基本功是否扎实(像我这样的就挂了)。

题意:你有一棵\(n\)个点的树,每次随机选择一条边,将这条边的两个端点合并,并随机继承两个点标号中的一个,问对于每一个点来说,最终剩下的那个点标号等于它的标号的概率。\(n\leq 50\),用浮点数方式输出。

碰到浮点数输出的题就很怕卡精,不过这道题似乎不卡,担心卡精可以开\(long \ double\)(还要吐槽一句cf的\(C++11\)对\(long\ double\)的输出好像不是很资瓷...还要转\(double\)输出)。

好了现在开始讲做法吧。我们的大体思想是每一个点分别求解答案。对于每一个点,用某种方法算出它最终被留下的方案数,那么再除以\((n-1)!\)显然就是答案。不过要注意的一点是因为标号的继承是随机的,因此对于同一种删边顺序,得到的结果可能不同,因此我们算出的其实是所有顺序下这个点保留的概率的总和(可能是浮点数),但是为了接下来表达的简便,不妨不严谨的称其为方案数。

现在来关心怎么求出每一个点被留下的方案数,我们将要求答案的点\(x\)当作树的根,并用\(size_i\)表示以\(i\)为根的子树的大小。考虑树形\(dp\),我们用\(f_{i,j}\)表示当根节点的标号继承到\(i\)点时,如果\(i\)的子树还剩下\(j\)条边,根节点的标号最终被保留下来的方案数。那么\(f_{x,n-1}\)就是我们想要的答案。

我们先来解决一个小问题:

假设我们将当前节点\(u\)的子树划分为两部分,并且已经知道了左半部分还剩\(i\)条边时的方案数\(a\)和右半部分还剩\(j\)条边时的方案数\(b\),如何求解它们对整棵子树还剩\(i+j\)条边的方案数的贡献?

显然左右两部分的子树对对方是没有影响的,因此我们可以将左右的方案合并。只要剩下的左边的\(i\)条边和右边的\(j\)条边在之后删除的相对顺序不变,那么一定会得到同一种结果,因此这部分合并的方案数就是\({{i+j}\choose i}\)种(即在删除序列的\(i+j\)个空位种选\(i\)个给左边的边)。

同时我们还要注意已经删除的边,在真实的操作序列中它们也同样需要合在一起。因此和上面相似,我们假设左边原来一共有\(x\)条边,右边原来一共有\(y\)条边,那么这部分合并的方案数就是\({{x+y-i-j}\choose x-i}\)。

综上所述,它们的贡献应该是\(a*b*{{i+j}\choose i}*{{x+y-i-j}\choose x-i}\)。

那么沿着刚刚的想法继续思考,我们或许可以采取如下策略\(dp\):对于某一棵以\(u\)为根的子树,不考虑任何子树时有\(f_{u,0}=1\)。假如我们有一种方法,可以计算出一个单点在只考虑一棵子树时的答案,那么我们的问题就做完了,因为我们在新考虑一棵子树的时候,我们可以先计算只考虑它时的答案而将其视为我们刚刚所讲的“右半部分”,将之前已经计算完的部分视为“左半部分”,就可以直接按照之前所讲的方法合并。

现在我们只要解决如何计算只考虑\(u\)的某一棵子树时的答案,设其根为\(v\)。显然我们可以枚举\(i\),表示我们想要求其还剩下\(i\)条边时的答案,设其为\(g_i\),接着再枚举\(j\),考虑\(f_{v,j}\)对\(g_i\)的贡献。分三类情况讨论:

\(1\)、假设\(j<i\),显然合法的过程应该是这样的:\(v\)的子树中合并到还剩\(i-1\)条边时,根的标号继承到了\(u\)上,接着\(v\)的子树中的边继续合并到只剩\(j\)条边,接着根的标号再从\(u\)继承到了\(v\)上。注意到\(u\)的标号继承到\(v\)上发生的概率是\(\frac{1}{2}\),因此此时\(f_{v,j}\)对\(g_i\)的贡献是\(\frac{1}{2}f_{v,j}\)。

\(2\)、假设\(j=i\),显然合法的过程应该是这样的:\(v\)的子树原来共有\(size_v-1\)条边,如果要剩下\(i\)条边,应该删除\(size_v-1-i\)条边,而\(u\)到\(v\)的连边也应该随着这些边的删除一起被删除,考虑被删除的\(size_v-1-i\)条边组成的序列,\(u\)到\(v\)的连边可以插入到\(size_v-i\)个空位(因为两端也是可以的)中的任何一个。同时我们可以发现如此一来,当根节点的标号继承到\(u\)时,\(u\)和\(v\)的连边已经消失,因此就不需要考虑那\(\frac{1}{2}\)的概率了,贡献是\((size_v-i)*f_{v,j}\)。

\(3\)、假设\(j>i\),画图考虑一下就发现这是没有合法方案的,贡献是\(0\)。

于是我们终于完成了最后一块拼图,得到了可行的解法。最后总结一下做法,我们分别计算每一个答案,接着进行树形\(dp\)。对于每一个新考虑的儿子,我们先计算只考虑这个子树的情况,接着将其与原有答案进行合并。计算一下复杂度,在每一个点更新它对父亲的贡献时似乎至多是\(O(n^2)\)的,但是考虑合并两个大小为\(x\)和\(y\)的子树,代价可以做到\(O(x*y)\),这等价于两个子树之间的点对数。因此一次dp的复杂度应该是总点对数即\(O(n^2)\),因此总复杂度是\(O(n^3)\)的。不过代码里我偷了个懒写了\(O(n^4)\)的做法,反正\(n\leq 50\)因此也是不要紧的。

我的代码:

  1. #include<cstdio>
  2. #include<vector>
  3. using std::vector;
  4. typedef long double ldb;
  5. const int N=55;
  6. int n;
  7. vector<int> G[N];
  8. int size[N];
  9. ldb fact[N];
  10. ldb dp[N][N],tmp[N],g[N];
  11. inline ldb choose(int n,int m)
  12. {
  13. return fact[n]/(fact[m]*fact[n-m]);
  14. }
  15. void dfs(int now,int father)
  16. {
  17. register int i,j;
  18. dp[now][0]=1;size[now]=1;
  19. for(auto x:G[now])
  20. {
  21. if(x==father)
  22. continue;
  23. dfs(x,now);
  24. for(i=0;i<=size[x];i++)
  25. {
  26. g[i]=0;
  27. for(j=1;j<=size[x];j++)
  28. if(j<=i)
  29. g[i]+=0.5*dp[x][j-1];
  30. else
  31. g[i]+=dp[x][i];
  32. }
  33. for(i=0;i<size[now]+size[x];i++)
  34. tmp[i]=0;
  35. for(i=0;i<size[now];i++)
  36. for(j=0;j<=size[x];j++)
  37. tmp[i+j]+=dp[now][i]*g[j]*choose(i+j,i)*choose(size[now]-1-i+size[x]-j,size[now]-1-i);
  38. for(i=0;i<size[now]+size[x];i++)
  39. dp[now][i]=tmp[i];
  40. size[now]+=size[x];
  41. }
  42. return;
  43. }
  44. signed main()
  45. {
  46. int x,y;
  47. register int i;
  48. scanf("%d",&n);
  49. fact[0]=1;
  50. for(i=1;i<=n-1;i++)
  51. fact[i]=fact[i-1]*i;
  52. for(i=1;i<=n-1;i++)
  53. {
  54. scanf("%d%d",&x,&y);
  55. G[x].push_back(y);
  56. G[y].push_back(x);
  57. }
  58. for(i=1;i<=n;i++)
  59. {
  60. dfs(i,0);
  61. printf("%.9lf\n",(double)(dp[i][n-1]/fact[n-1]));
  62. }
  63. return 0;
  64. }

Codeforces 1060 F. Shrinking Tree的更多相关文章

  1. Codeforces Round #375 (Div. 2) F. st-Spanning Tree 生成树

    F. st-Spanning Tree 题目连接: http://codeforces.com/contest/723/problem/F Description You are given an u ...

  2. Codeforces 1129 E.Legendary Tree

    Codeforces 1129 E.Legendary Tree 解题思路: 这题好厉害,我来复读一下官方题解,顺便补充几句. 首先,可以通过询问 \(n-1​\) 次 \((S=\{1\},T=\{ ...

  3. Codeforces Round #781(C. Tree Infection)

    Codeforces Round #781 C. Tree Infection time limit per test 1 second memory limit per test 256 megab ...

  4. Codeforces 461B Appleman and Tree(木dp)

    题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每一个节点的父亲节点,以及每一个点的颜色(0表示白色,1表示黑色),切断这棵树的k ...

  5. Codeforces 959 F. Mahmoud and Ehab and yet another xor task

    \(>Codeforces\space959 F. Mahmoud\ and\ Ehab\ and\ yet\ another\ xor\ task<\) 题目大意 : 给出一个长度为 \ ...

  6. Codeforces 835 F. Roads in the Kingdom

    \(>Codeforces\space835 F. Roads in the Kingdom<\) 题目大意 : 给你一棵 \(n\) 个点构成的树基环树,你需要删掉一条环边,使其变成一颗 ...

  7. Codeforces 280C Game on tree【概率DP】

    Codeforces 280C Game on tree LINK 题目大意:给你一棵树,1号节点是根,每次等概率选择没有被染黑的一个节点染黑其所有子树中的节点,问染黑所有节点的期望次数 #inclu ...

  8. Codeforces A. Game on Tree(期望dfs)

    题目描述: Game on Tree time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  9. Codeforces 731 F. Video Cards(前缀和)

    Codeforces 731 F. Video Cards 题目大意:给一组数,从中选一个数作lead,要求其他所有数减少为其倍数,再求和.问所求和的最大值. 思路:统计每个数字出现的个数,再做前缀和 ...

随机推荐

  1. 菜鸟vimer成长记——第2.4章、cmd-line模式

    cmd-line模式又有3个类型:Ex 命令(ex commands).查找模式(Search patterns).Filter 命令(Filter commands).本文主要重点的是Ex 命令和S ...

  2. Model详解

    一.数据库操作 Model操作: 创建数据库表结构(建表) 操作数据库表(增删改查) 做一部分的验证(验证) a.建表 1.表字段 AutoField(Field) - int自增列,必须填入参数 p ...

  3. Microsoft Visual Studio International Pack

    Visual Studio International Pack 包含一组类库,该类库扩展了.NET Framework对全球化软件开发的支持.使用该类库提供的类,.NET 开发人员可以更方便的创建支 ...

  4. 大话 .Net 之内存管理

    在一次偶然的机会中,我来到了恒生的大家庭.又在一次偶然的机会中,我很荣幸的被勇哥信任并让我写一篇季刊的文章.可能人生之中充满了无数次的偶然机会,我们只有抓住眼前的“偶然”,才可以创建人生.当我接到这个 ...

  5. java随记

    jdk1.8中新增了 LocalDate 与 LocalDateTime等类来解决日期处理方法,同时引入了一个新的类DateTimeFormatter来解决日期格式化问题. LocalDateTime ...

  6. Fiddler 抓包浅析

    Fiddler 工具浅析 Fiddler 是位于客户端和服务器端的 HTTP 代理,也是目前最常用的 HTTP 抓包工具之一.(Mac OS 建议采用 Charles) 它可以记录客户端和服务器之间的 ...

  7. [Codeforces-888C] - K-Dominant Character

    C. K-Dominant Character time limit per test 2 seconds memory limit per test 256 megabytes input stan ...

  8. 关于docker线上部署时间问题

    背景 公司线上部署采用docker swarm方式,这几天线上项目时间突然出了问题(ps:第一反应,我去,这也能出问题,代码里肯定藏毒了),线上时间总跟实际时间差八个小时.本着速战速决的原则,把所有时 ...

  9. 【转】利用telnet来进行调试Skynet

    https://blog.csdn.net/WhereIsHeroFrom/article/details/80674408

  10. python编辑修改haproxy配置文件--文件基础操作

    一.需求分析 有查询,删除,添加的功能 查询功能:查询则打印查询内容,如果不存在也要打印相应的信息 删除功能:查询到要删除内容则删除,打印信息. 添加功能:同上. 二.流程图 三.代码实现 本程序主要 ...