一道概率神题,考试时没读清题考完看了学长的玄学题解看了好几个小时

首先f[i][j]表示在点 i 为根的子树中,向下最长轻链长度小于等于 j 的概率。

首先递归下去并求出子树大小,然后枚举重儿子,枚举该点最长轻链长度,再次枚举儿子节点并逐个

假设当前枚举的重儿子是to1,枚举到儿子节点to2,x最长轻链长度为k,设gs为v(to2)之前考虑的儿子中最长轻链长度为k的概率如果v(to1)=v(to2)即v(to2)为重儿子,则设fs为以v(to2)为根的子树最长轻链长度为k的概率:

h[k]=(fs*g[k]%mod+gs*f[to2][k]%mod-gs*fs+mod)%mod;     

如果v(to2)是轻儿子,则设fs为以v(to2)为根的子树最长轻链长度为k-1的概率,

h[k]=(fs*g[k]%mod+gs*f[to2][k-1]%mod-gs*fs+mod)%mod;

只是x与to2相连的这条边为轻链所以有减1,值得提醒的一点是这里的f[x][k]并不是最终的f[x][k],只是考虑到当前几个儿子时的值,一个儿子一个儿子地向里加。考虑到f数组直接改的话会错,所以用h数组保存,最后加到g数组中清空h,当to1为重儿子这个情况考虑玩后将g数组加到f中去,清空g。当前节点x求完后,此时的f数组并不是前缀和,所以需要再次转化。

最后求答案时再次将前缀和转化为单个的值

至于此题为啥求概率却用一堆整数想乘是因为题目要求

我们发现每一层的1/chu[x]即为分母,所以可以直接乘逆元,而这样的相加不会影响结果

所以最后i*f[x][i]就是期望。

  1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<string>
5 #include<algorithm>
6 #include<vector>
7 #include<cmath>
8 #include<stack>
9 #include<queue>
10 #define MAXN 3101
11 #define ll long long
12 using namespace std;
13 const ll mod=1e9+7;
14 struct node{ll to,n;}e[MAXN];
15 ll head[MAXN],tot;
16 void add(ll u,ll v){e[++tot].to=v;e[tot].n=head[u];head[u]=tot;}
17 ll size[MAXN];
18 ll f[MAXN][MAXN],g[MAXN],h[MAXN];
19 ll n;ll chu[MAXN];
20 ll pow(ll x,ll y)
21 {
22 ll ans=1;
23 while(y)
24 {
25 if(y&1)ans=(ans*x)%mod;
26 x=(x*x)%mod;
27 y>>=1;
28 }
29 return ans%mod;
30 }
31 void DFS(ll x)
32 {
33 size[x]=1;
34 for(ll i=head[x];i;i=e[i].n)
35 {
36 ll to=e[i].to;
37 DFS(to);
38 size[x]+=size[to];
39 }
40 ll ppow=pow(chu[x],mod-2ll)%mod;
41 for(ll i=head[x];i;i=e[i].n)
42 {
43 for(ll i=0;i<=size[x]+1;++i)g[i]=1;
44 for(ll j=head[x];j;j=e[j].n)
45 {
46 ll fs,gs;
47 ll to1=e[i].to;ll to2=e[j].to;
48 for(ll k=0;k<=size[to2]+1;++k)
49 {
50 if(to1==to2){
51 if(!k)fs=f[to2][k];else fs=f[to2][k]-f[to2][k-1];
52 if(!k)gs=g[k]; else gs=g[k]-g[k-1];
53 h[k]=(fs*g[k]%mod+gs*f[to2][k]%mod-gs*fs+mod)%mod;
54 // printf("h[%lld]=%lld\n",k,h[k]);
55 }
56 else if(k){
57 if(!k)fs=f[to2][k-1];else fs=f[to2][k-1]-f[to2][k-2];
58 if(!k)gs=g[k]; else gs=g[k]-g[k-1];
59 h[k]=(fs*g[k]%mod+gs*f[to2][k-1]%mod-gs*fs+mod)%mod;
60 }
61 }
62 g[0]=h[0];h[0]=0;
63 for(ll k=1;k<=size[to2]+1;++k)
64 {
65 g[k]=(g[k-1]+h[k])%mod;h[k]=0;
66 }
67 }
68 for(ll j=size[x]+1;j>=1;--j)
69 {
70 g[j]=(g[j]-g[j-1]+mod)%mod;//printf("g[%lld]=%lld\n",j,g[j]);
71 }
72 for(ll j=0;j<=size[x]+1;++j)
73 {
74 f[x][j]=(f[x][j]+g[j]*ppow%mod)%mod;
75 // printf("f[%lld][%lld]=%lld\n",x,j,f[x][j]);
76 }
77 // printf("p=%lld\n",pow(chu[x],mod-2ll));
78 }
79 if(head[x]==0)f[x][0]=1;
80 for(ll i=1;i<=size[x]+1;++i)
81 {
82 f[x][i]=(f[x][i]+f[x][i-1]+mod)%mod;
83 // printf("f[%lld][%lld]=%lld\n",x,i,f[x][i]);
84 }
85 }
86 ll ru[MAXN];ll si=1;
87 int main()
88 {
89 scanf("%lld",&n);
90 for(ll i=1;i<=n;++i){
91 scanf("%lld",&chu[i]);
92 for(ll j=1;j<=chu[i];++j){
93 ll y;
94 scanf("%lld",&y);
95 add(i,y);ru[y]++;
96 }
97 }
98 ll root=0;
99 for(ll i=1;i<=n;++i){
100 if(ru[i]==0)
101 root=i;
102 }
103 DFS(root);
104 ll ans=0;
105 for(ll i=1;i<=size[root]+1;++i)
106 {
107 ans=(ans+i*(f[root][i]-f[root][i-1]%mod)+mod)%mod;
108 }
109 printf("%lld\n",(ans+mod)%mod);
110 }

【模拟7.14】B. 熟练剖分(tree) (概率DP)的更多相关文章

  1. 熟练剖分(tree) 树形DP

    熟练剖分(tree) 树形DP 题目描述 题目传送门 分析 我们设\(f[i][j]\)为以\(i\)为根节点的子树中最坏时间复杂度小于等于\(j\)的概率 设\(g[i][j]\)为当前扫到的以\( ...

  2. 20210501 序列,熟练剖分(tree),建造游乐园(play)

    考场 \(65+5+0\),并列 rk2 最高分 \(55+10+10\) T1:等比数列可以写作 \(q^kx\),发现 \(q\le1000\) 且有一档分为 \(a_i\le100\),想到 \ ...

  3. HZOI2019熟练剖分(tree)

    题目大意:https://www.cnblogs.com/Juve/articles/11186805.html 题解: 先给出官方题解: 其实这题跟期望没什么关系,因为E=$\sum_\limits ...

  4. 【XSY2332】Randomized Binary Search Tree 概率DP FFT

    题目描述 \(\forall 0\leq i<n\),求有多少棵\(n\)个点,权值和优先级完全随机的treap的树高为\(i\). \(n\leq 30000\) 题解 设\(f_{i,j}\ ...

  5. [CSP-S模拟测试]:石头剪刀布(rps)(概率DP)

    题目传送门(内部题9) 输入格式 第一行一个整数$n$.接下来$n$行每行$3$个非负整数$r_i,p_i,s_i$. 输出格式 一行一个实数表示答案.当你的答案与标准答案的绝对或相对误差不超过${1 ...

  6. NOIP模拟测试3「序列·熟练剖分·建造游乐园(play)」

    ---恢复内容开始--- 序列 刚调出来样例就A了,假装是水题. 因为是乱序,我们要求出来每两项之间最小公比,而不是直接比 求出来每两项之间最小公比,然后扫一遍就完了.(还要注意重复情况) 那么问题就 ...

  7. 20190716NOIP模拟赛T1 礼物(概率dp+状压)

    题目描述 夏川的生日就要到了.作为夏川形式上的男朋友,季堂打算给夏川买一些生 日礼物. 商店里一共有种礼物.夏川每得到一种礼物,就会获得相应喜悦值Wi(每种 礼物的喜悦值不能重复获得). 每次,店员会 ...

  8. [CSP-S模拟测试]:糊涂图(概率DP)

    题目传送门(内部题76) 输入格式 第一行输入三个空格隔开的整数$n,m,s$表示随机加一条边之前的糊涂图的点数,边数,以及起点的编号. 接下来$m$行,每行两个空格隔开的整数$a,b$表示从$a$到 ...

  9. JZOJ 【NOIP2017提高A组模拟9.14】捕老鼠

    JZOJ [NOIP2017提高A组模拟9.14]捕老鼠 题目 Description 为了加快社会主义现代化,建设新农村,农夫约(Farmer Jo)决定给农庄里的仓库灭灭鼠.于是,猫被农夫约派去捕 ...

随机推荐

  1. 03.14 ICPC训练联盟周赛,Preliminaries for Benelux Algorithm Programming Contest 2019

    A .Architecture 题意:其实就是想让你找到两行数的最大值,然后比较是否相同,如果相同输出'possible',不同则输出'impossible' 思路:直接遍历寻找最大值,然后比较即可 ...

  2. Matlab将数据存为文本文件

    dlmwrite :将一个矩阵写到由分隔符分割的文件中. 在保存整数到文件时使用save存为ascii文件时,常常是文件里都是实型格式的数据(有小数点,和后面很多的0,看着很不方便).于是要保存此类数 ...

  3. ThreadLocal内存溢出代码演示和原因分析!

    ThreadLocal 翻译成中文是线程本地变量的意思,也就是说它是线程中的私有变量,每个线程只能操作自己的私有变量,所以不会造成线程不安全的问题. ​ 线程不安全是指,多个线程在同一时刻对同一个全局 ...

  4. Django(34)Django操作session(超详细)

    前言 session: session和cookie的作用有点类似,都是为了存储用户相关的信息.不同的是,cookie是存储在本地浏览器,session是一个思路.一个概念.一个服务器存储授权信息的解 ...

  5. [bug] sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1054, "Unknown column 'recevie_name' in 'field list'")

    Python Flask 开发购物网站,提交订单时报错 根据提示,检查代码,发现是字段名拼写错误导致,数据库对应的字段是receive_name,误写成了recevie_name 另外要注意,灰色字和 ...

  6. 微信收藏了很多语音,有一些比较有意义的,但是发现只能收藏在微信,没有办法导出了,请大神看清楚,是微信【收藏】的语音,ios或者安卓的方法都可以

  7. Linux_ACL文件访问控制列表

    一.ACL文件访问控制列表 前言 1️⃣:ACL-文件访问控制列表: 2️⃣:ACL可以针对单个用户,单个文件或目录来进行r.w.x的权限设定,特别适用于需要特殊权限的使用情况. 3️⃣:ACL就是可 ...

  8. C++知识点案例 笔记-5

    1.关系运算符重载 2.类型转换函数重载 3.转换构造函数 4.函数模板 5.显式实例化 6.类模板外定义模板函数 1.关系运算符重载 ==关系运算符重载== //直接(按分数)比较两个对象 #inc ...

  9. Node.js入门(含NVM、NPM、NVM的安装)-(转载)

    Node.js的介绍 引擎 引擎的特性: JS的内核即引擎.因为引擎有以下特性: (1)转化的作用: 汽油柴油等等->动能 模板+数据--->页面 js引擎:js 代码--->机器码 ...

  10. IDEA 常用快捷键列表【建议收藏】

    编辑代码 快捷键 说明 Alt+Enter 导入包.自动变量命名等(万能快捷键) Ctrl+X 删除行 Ctrl+Y 删除当前行 Ctrl+D 复制行 Alt+Shift+Up/Down或Ctrl+S ...